/** * Copyright (C) 2001-3, Anthony Harrison anh23@pitt.edu This library is free * software; you can redistribute it and/or modify it under the terms of the GNU * Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jactr.core.chunktype.basic; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.TreeSet; import java.util.concurrent.ConcurrentSkipListSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunktype.IChunkType; import org.jactr.core.chunktype.IRemovableSymbolicChunkType; import org.jactr.core.chunktype.IllegalChunkTypeStateException; import org.jactr.core.chunktype.event.ChunkTypeEvent; import org.jactr.core.module.declarative.IDeclarativeModule; import org.jactr.core.slot.IMutableSlot; import org.jactr.core.slot.ISlot; import org.jactr.core.slot.NotifyingSlotContainer; import org.jactr.core.utils.collections.ChunkNameComparator; /** * default impl. All slots and parenting must be resolved before the given * chunktype is encoded. parents must be encoded as well. chunks, however, can * be added after encoding * * @author harrison * @created January 22, 2003 */ public class BasicSymbolicChunkType extends NotifyingSlotContainer implements IRemovableSymbolicChunkType { final private static transient Log LOGGER = LogFactory .getLog(BasicSymbolicChunkType.class .getName()); private static int TOTAL_COUNT = 0; protected Collection<IChunkType> _children; protected Collection<IChunk> _chunks; protected String _name; /** * This refers to the IChunkType object that this is the symbolic portion of. * This is not to be confused with the supertype chunktype which is the * IChunkType that this _parentChunkType was derived from.. * * @since */ protected IChunkType _parentChunkType; protected Collection<IChunkType> _supertypeParents; /** * Constructor for the DefaultSymbolicChunkType5 object */ public BasicSymbolicChunkType() { _children = new TreeSet<IChunkType>(); // _chunks = new FastSet<IChunk>(); // was array list, behold perf // improvments _chunks = new ConcurrentSkipListSet<IChunk>(new ChunkNameComparator()); _supertypeParents = new ArrayList<IChunkType>(2); ++TOTAL_COUNT; } // public BasicSymbolicChunkType(AbstractChunkType parentChunkType, // Collection<IChunkType> superParentTypes) // { // this(parentChunkType); // for (IChunkType parent : superParentTypes) // addParent(parent); // } public void bind(IChunkType wrapper, Collection<IChunkType> parents) { _parentChunkType = wrapper; _supertypeParents.clear(); _chunks.clear(); _children.clear(); clear(); for (IChunkType parent : parents) addParent(parent); } /** * */ @Override public void dispose() { IDeclarativeModule decM = _parentChunkType.getModel() .getDeclarativeModule(); for (IChunk chunk : _chunks) if (!chunk.hasBeenDisposed()) decM.dispose(chunk); _chunks.clear(); _chunks = null; for (IChunkType child : _children) child.dispose(); _children.clear(); _children = null; super.dispose(); _supertypeParents.clear(); _supertypeParents = null; _parentChunkType = null; } /** * @see org.jactr.core.chunktype.ISymbolicChunkType#getParent() */ public Collection<IChunkType> getParents() { return Collections.unmodifiableCollection(_supertypeParents); } public IChunkType getParent() { int size = _supertypeParents.size(); if (size > 1) throw new IllegalStateException(String.format( "%s has multiple parents, don't assume single parentage", getName())); if (size == 0) return null; return _supertypeParents.iterator().next(); } public int getNumberOfParents() { return _supertypeParents.size(); } /** * @see org.jactr.core.chunktype.ISymbolicChunkType#getName() */ public String getName() { if (_name == null) _name = String.format("ChunkType-%d", TOTAL_COUNT); return _name; } /** * */ public void setName(String name) { if (_parentChunkType.isEncoded()) throw new IllegalChunkTypeStateException( "cannot change the name of encoded chunktypes"); _name = name; } /** * grab the slots of the parent and the children */ public IChunkType addParent(IChunkType ct) { if (_supertypeParents.contains(ct)) return _parentChunkType; if (_parentChunkType.isEncoded()) throw new IllegalChunkTypeStateException( "cannot add parent to encoded chunktype"); if (LOGGER.isDebugEnabled()) LOGGER.debug("Adding parent chunk type = " + ct + " to type " + _name); _supertypeParents.add(ct); /* * copy all his slots */ for (ISlot slot : ct.getSymbolicChunkType().getSlots()) addSlot(slot); return _parentChunkType; } /** * return all the chunktypes that are directly decended from this one */ public Collection<IChunkType> getChildren() { return Collections.unmodifiableCollection(_children); } public int getNumberOfChildren() { return _children.size(); } /** * @see org.jactr.core.chunktype.ISymbolicChunkType#addChild(org.jactr.core.chunktype.IChunkType) */ public void addChild(IChunkType ct) { /* * ct had better not have any chunks */ if (ct.getSymbolicChunkType().getNumberOfChunks() != 0) throw new IllegalChunkTypeStateException( "Cannot add a child that already has chunks"); if (LOGGER.isDebugEnabled()) LOGGER.debug("Adding child chunktype = " + ct.getSymbolicChunkType().getName() + " to " + _name); /* * we don't notify the parent since children is only concerned with the * immediate children we also need to check to make sure no duplicates, b/c * of multiple inheritance this may get called more than once for each chunk * type. */ if (!_children.contains(ct)) _children.add(ct); if (_parentChunkType.hasListeners()) _parentChunkType.dispatch(new ChunkTypeEvent(_parentChunkType, ct)); } /** * return all chunks of this type */ public Collection<IChunk> getChunks() { return Collections.unmodifiableCollection(_chunks); } /** * add this chunk to this chunktype and then up the parental hierachy * * @see org.jactr.core.chunktype.ISymbolicChunkType#addChunk(org.jactr.core.chunk.IChunk) */ public void addChunk(IChunk c) { if (LOGGER.isDebugEnabled()) if (LOGGER.isDebugEnabled()) LOGGER.debug(this + ": Adding chunk = " + c + " to " + _name); _chunks.add(c); if (_supertypeParents.size() > 0) for (IChunkType parent : _supertypeParents) parent.getSymbolicChunkType().addChunk(c); if (_parentChunkType.hasListeners()) _parentChunkType.dispatch(new ChunkTypeEvent(_parentChunkType, c)); } /** */ public int getNumberOfChunks() { return _chunks.size(); } /** */ public boolean hasSlot(ISlot s) { return getSlot(s.getName()) != null; } /** * Yes, you can modify the slots (add/remove) but off if not encoded */ public boolean canModify() { return !_parentChunkType.isEncoded(); } /** * Gets the authoritative attribute of the DefaultSymbolicChunkType5 object * * @param slot * Description of the Parameter * @return The authoritative value */ public boolean isAuthoritative(ISlot slot) { if (_supertypeParents.size() == 0) return true; if (hasSlot(slot)) { for (IChunkType parent : _supertypeParents) if (parent.getSymbolicChunkType().getSlot(slot.getName()) != null) return false; } else return false; return true; } /** * Adds a feature to the ISlot attribute of the DefaultSymbolicChunkType5 * object * * @param s * The feature to be added to the ISlot attribute * @since */ @Override public void addSlot(ISlot s) { if (_parentChunkType.isEncoded()) throw new IllegalChunkTypeStateException( "cannot add slots after encoding"); if (LOGGER.isDebugEnabled()) LOGGER.debug(this + ":Adding slot " + s + " to " + _name); IMutableSlot oldSlot = (IMutableSlot) getSlot(s.getName()); if (oldSlot != null) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Overriding existing slot " + oldSlot + " with new value " + s.getValue()); oldSlot.setValue(s.getValue()); } else super.addSlot(s); if (_parentChunkType.hasListeners()) _parentChunkType.dispatch(new ChunkTypeEvent(_parentChunkType, ChunkTypeEvent.Type.SLOT_ADDED, s)); } /** * Description of the Method * * @param s * Description of Parameter * @since */ @Override public void removeSlot(ISlot s) { if (_parentChunkType.isEncoded()) throw new IllegalChunkTypeStateException( "cannot remove slots after encoding"); if (LOGGER.isDebugEnabled()) LOGGER.debug(this + ":Removing slot " + s); super.removeSlot(s); if (_parentChunkType.hasListeners()) _parentChunkType.dispatch(new ChunkTypeEvent(_parentChunkType, ChunkTypeEvent.Type.SLOT_REMOVED, s)); } /** * @return true if this chunktype is ct or derived from ct */ public boolean isA(IChunkType ct) { if (ct == null) return false; // AMH 7/19/06 was true if (ct == _parentChunkType) return true; else for (IChunkType parent : _supertypeParents) if (parent.getSymbolicChunkType().isA(ct)) return true; return false; } /** * Description of the Method * * @return Description of the Return Value */ @Override public String toString() { return "Sym:" + getName(); } public void encode() { // noop } public Object getAdapter(Class adapterClass) { if (adapterClass.isAssignableFrom(getClass())) return this; return null; } /** * remove this chunk from the chunktype's list of encoded chunks. No event is * fired. */ public void removeChunk(IChunk chunk) { if (LOGGER.isDebugEnabled()) if (LOGGER.isDebugEnabled()) LOGGER.debug(this + ": Removing chunk = " + chunk + " to " + _name); _chunks.remove(chunk); if (_supertypeParents.size() > 0) for (IChunkType parent : _supertypeParents) if (parent.getSymbolicChunkType() instanceof IRemovableSymbolicChunkType) ((IRemovableSymbolicChunkType) parent.getSymbolicChunkType()) .removeChunk(chunk); } /** * noop for now */ public void removeChild(IChunkType chunkType) { if (LOGGER.isWarnEnabled()) LOGGER.warn(String.format("removeChild is a noop at this time")); } /** * Noop for now. */ public void removeParent(IChunkType chunkType) { if (LOGGER.isWarnEnabled()) LOGGER.warn(String.format("removeParent is a noop at this time")); } }