/**
* 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.chunk.basic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.chunk.ISymbolicChunk;
import org.jactr.core.chunk.IllegalChunkStateException;
import org.jactr.core.chunktype.IChunkType;
import org.jactr.core.slot.ChunkSlot;
import org.jactr.core.slot.IMutableSlot;
import org.jactr.core.slot.ISlot;
/**
* default symbolic chunk implementation
*
* @author harrison
* @created February 5, 2003
*/
public class BasicSymbolicChunk implements ISymbolicChunk
{
private static transient Log LOGGER = LogFactory
.getLog(BasicSymbolicChunk.class
.getName());
protected IChunkType _chunkType;
protected String _chunkName;
final protected IChunk _parentChunk;
/**
* slots are often accessed both individually and as a collection so this
* cachedMap makes sense
*/
protected Map<String, ChunkSlot> _slotMap;
private static int TOTAL_COUNT = 0;
public BasicSymbolicChunk(IChunk parentChunk, IChunkType ct)
{
TOTAL_COUNT++;
_slotMap = new TreeMap<String, ChunkSlot>();
_parentChunk = parentChunk;
setChunkType(ct);
}
/**
* create a symbolicchunk that has the same values as reference this is used
* when making copies of the chunk
*/
protected BasicSymbolicChunk(IChunk realParent, ISymbolicChunk reference)
{
this(realParent, reference.getChunkType());
/*
* copy the slot values
*/
for (IMutableSlot slot : _slotMap.values())
slot.setValue(reference.getSlot(slot.getName()).getValue());
setName(reference.getName() + "-" + TOTAL_COUNT);
}
protected Lock readLock()
{
return _parentChunk.getReadLock();
}
protected Lock writeLock()
{
return _parentChunk.getWriteLock();
}
/**
* cannot add/remove slots from a chunk
*/
public boolean canModify()
{
return false;
}
/**
* clear resources
*/
public void dispose()
{
try
{
writeLock().lock();
_slotMap.clear();
}
finally
{
writeLock().unlock();
}
}
/**
* return the IChunk wrapper
*/
public IChunk getParentChunk()
{
return _parentChunk;
}
/**
* no op
*/
public void encode(double when)
{
}
/**
* @see org.jactr.core.chunk.ISymbolicChunk#getName()
*/
public String getName()
{
if (_chunkName == null || _chunkName == "")
setName(_chunkType.getSymbolicChunkType().getName() + "-" + TOTAL_COUNT);
try
{
readLock().lock();
return _chunkName;
}
finally
{
readLock().unlock();
}
}
/**
* set the chunk name, once encoded this will fail
*/
public void setName(String name)
{
if (_parentChunk.isEncoded())
throw new IllegalChunkStateException(
"Cannot change chunks name once encoded");
try
{
writeLock().lock();
if (_chunkName != null && _chunkName.equals(name)) return;
if (LOGGER.isDebugEnabled()) LOGGER.debug("Setting chunk name " + name);
_chunkName = name;
}
finally
{
writeLock().unlock();
}
}
/**
* @see org.jactr.core.chunk.ISymbolicChunk#getChunkType()
*/
public IChunkType getChunkType()
{
return _chunkType;
}
/**
* @param ct
*/
private void setChunkType(IChunkType ct)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Setting chunk type " + ct.getSymbolicChunkType().getName());
if (_chunkType != null)
throw new IllegalChunkStateException(
"Cannot overwrite parent IChunkType in chunk " + getName());
try
{
writeLock().lock();
_chunkType = ct;
for (ISlot slot : _chunkType.getSymbolicChunkType().getSlots())
addSlotInternal(slot);
}
finally
{
writeLock().unlock();
}
}
/**
* return a collection of the ACTUAL slots
*
* @see org.jactr.core.slot.ISlotContainer#getSlots()
*/
public Collection<? extends ISlot> getSlots()
{
/*
* no need to lock since the slots never add or remove
*/
return Collections.unmodifiableCollection(_slotMap.values());
}
/**
* non-locking since the number of slots never changes
*
* @param slots
* @return
* @see org.jactr.core.slot.ISlotContainer#getSlots(java.util.Collection)
*/
public Collection<ISlot> getSlots(Collection<ISlot> slots)
{
if (slots == null) slots = new ArrayList<ISlot>(_slotMap.size());
slots.addAll(_slotMap.values());
return slots;
}
/**
* add a slot by copying it first. Not locked since this is only called from
* the constructor
*
* @param s
*/
private void addSlotInternal(ISlot s)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("adding slot " + s);
ChunkSlot cs = new ChunkSlot(s, _parentChunk);
_slotMap.put(cs.getName().toLowerCase(), cs);
}
/**
* will set the value of a slot You cannot add slots to a chunk, only the
* chunk-type - but this will set the value, assuming the slot exists
*/
public void addSlot(ISlot s)
{
/**
* not locked, ChunkSlot will do this for us.
*/
((IMutableSlot) getSlot(s.getName())).setValue(s.getValue());
}
/**
* noop
*
* @see org.jactr.core.slot.ISlotContainer#removeSlot(org.jactr.core.slot.ISlot)
*/
public void removeSlot(ISlot s)
{
// NoOp chunks cant remove slots
}
/**
* @see org.jactr.core.chunk.ISymbolicChunk#isA(org.jactr.core.chunktype.IChunkType)
*/
public boolean isA(IChunkType ct)
{
if (_chunkType != null) return _chunkType.isA(ct);
return false;
}
/**
* @see org.jactr.core.chunk.ISymbolicChunk#isAStrict(org.jactr.core.chunktype.IChunkType)
*/
public boolean isAStrict(IChunkType ct)
{
return ct == _chunkType;
}
/**
* return the actual chunk slot backing the map. Not locked since the contents
* of the slot map never change
*
* @param slotName
* @return
*/
private ChunkSlot getSlotInternal(String slotName)
{
return _slotMap.get(slotName.toLowerCase());
}
/**
* return the actual slot
*/
public ISlot getSlot(String slotName)
{
ChunkSlot s = getSlotInternal(slotName);
if (s == null)
throw new IllegalChunkStateException(getName() + " of type "
+ getChunkType() + " does not contain a slot named " + slotName
+ " possible " + _slotMap.keySet());
return s;
}
@Override
public String toString()
{
return getName();
}
}