/*
* Created on Aug 3, 2005 Copyright (C) 2001-5, Anthony Harrison anh23@pitt.edu
* (jactr.org) 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.concurrent.Executor;
import java.util.concurrent.locks.Lock;
import org.jactr.core.chunk.IChunk;
import org.jactr.core.chunk.ISubsymbolicChunk;
import org.jactr.core.chunk.ISymbolicChunk;
import org.jactr.core.chunk.IllegalChunkStateException;
import org.jactr.core.chunk.event.ChunkEvent;
import org.jactr.core.chunk.event.IChunkListener;
import org.jactr.core.chunktype.IChunkType;
import org.jactr.core.event.IParameterListener;
import org.jactr.core.event.ParameterEvent;
import org.jactr.core.model.IModel;
import org.jactr.core.slot.ISlot;
/**
* abstract chunk that handles most common logic for the developer.
*
* @author harrison
*/
public abstract class AbstractChunk implements IChunk
{
/**
* chunk data used to store meta data event dispatchers and the like.
* this is volatile because it may change after a call to {@link #replaceContents(IChunk)}
* so we do not want the value to be cached.
*/
private volatile ChunkData _chunkData;
public AbstractChunk(IModel model)
{
_chunkData = new ChunkData(model);
}
/**
* @return
* @see org.jactr.core.chunk.IChunk#getModel()
*/
public IModel getModel()
{
return _chunkData.getModel();
}
/**
* @param chunkEvent
* @see org.jactr.core.chunk.IChunk#dispatch(org.jactr.core.chunk.event.ChunkEvent)
*/
public void dispatch(ChunkEvent chunkEvent)
{
_chunkData.getChunkDispatcher().fire(chunkEvent);
}
/**
* @param pEvent
* @see org.jactr.core.chunk.IChunk#dispatch(org.jactr.core.event.ParameterEvent)
*/
public void dispatch(ParameterEvent pEvent)
{
_chunkData.getParameterDispatcher().fire(pEvent);
}
/**
* note : non-locking
*
* @return
* @see org.jactr.core.chunk.IChunk#hasParameterListeners()
*/
public boolean hasParameterListeners()
{
return _chunkData.getParameterDispatcher().hasListeners();
}
/**
* note : non-locking
*
* @return
* @see org.jactr.core.chunk.IChunk#hasListeners()
*/
public boolean hasListeners()
{
return _chunkData.getChunkDispatcher().hasListeners();
}
/**
* note : non-locking
*
* @param cl
* @param executor
* @see org.jactr.core.chunk.IChunk#addListener(org.jactr.core.chunk.event.IChunkListener,
* java.util.concurrent.Executor)
*/
public void addListener(IChunkListener cl, Executor executor)
{
_chunkData.getChunkDispatcher().addListener(cl, executor);
}
/**
* note : non-locking
*
* @param pl
* @param executor
* @see org.jactr.core.chunk.IChunk#addListener(org.jactr.core.event.IParameterListener,
* java.util.concurrent.Executor)
*/
public void addListener(IParameterListener pl, Executor executor)
{
_chunkData.getParameterDispatcher().addListener(pl, executor);
}
/**
* note : non-locking
*
* @param cl
* @see org.jactr.core.chunk.IChunk#removeListener(org.jactr.core.chunk.event.IChunkListener)
*/
public void removeListener(IChunkListener cl)
{
_chunkData.getChunkDispatcher().removeListener(cl);
}
/**
* note : non-locking
*
* @param pl
* @see org.jactr.core.chunk.IChunk#removeListener(org.jactr.core.event.IParameterListener)
*/
public void removeListener(IParameterListener pl)
{
_chunkData.getParameterDispatcher().removeListener(pl);
}
/**
* @return
* @see org.jactr.core.chunk.IChunk#getSubsymbolicChunk()
*/
abstract public ISubsymbolicChunk getSubsymbolicChunk();
/**
* @return
* @see org.jactr.core.chunk.IChunk#getSymbolicChunk()
*/
abstract public ISymbolicChunk getSymbolicChunk();
/**
* replace the contents of this chunk with those of masterChunk. this is done
* after the merge is complete. note : locking
*
* @param masterChunk
*/
public void replaceContents(IChunk masterChunk)
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
Lock originalWriteLock = getWriteLock();
try
{
originalWriteLock.lock();
if (masterChunk instanceof AbstractChunk)
{
AbstractChunk mc = (AbstractChunk) masterChunk;
_chunkData.dispose();
_chunkData = mc._chunkData;
}
else
{
_chunkData.setMutable(masterChunk.isMutable());
_chunkData.setEncoded(masterChunk.isEncoded());
_chunkData.setComment(masterChunk.getComment());
/*
* copy the meta data
*/
for (String key : masterChunk.getMetaDataKeys())
setMetaData(key, masterChunk.getMetaData(key));
}
}
finally
{
/*
* the lock may have changed if the master was abstract.
* if someone snagged the lock to this chunk after this lock was
* acquired, but before the merge, they would be stuck since
* originalWriteLock would not be released if it were replaced.
*/
originalWriteLock.unlock();
}
}
/**
* note : non-locking
*
* @param comparison
* @return
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(IChunk comparison)
{
return getSymbolicChunk().getName().compareTo(
comparison.getSymbolicChunk().getName());
}
/**
* @see org.jactr.core.chunk.IChunk#dispose()
*/
public void dispose()
{
if (hasBeenDisposed())
throw new IllegalChunkStateException("Cant dispose of " + this
+ ", already been disposed!");
try
{
getWriteLock().lock();
if (!hasBeenDisposed()) _chunkData.dispose();
}
finally
{
getWriteLock().unlock();
}
}
/**
* note : non-locking
*
* @return
* @see org.jactr.core.chunk.IChunk#hasBeenDisposed()
*/
public boolean hasBeenDisposed()
{
return _chunkData.isDisposed();
}
/**
* note : non-locking
*
* @see org.jactr.core.chunk.IChunk#encode(double)
*/
public void encode(double when)
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
if (isEncoded()) return;
/*
* we cant actually lock here since the encoding process may trigger some
* internal events, such as parameter events
*/
getSymbolicChunk().encode(when);
getSubsymbolicChunk().encode(when);
_chunkData.setEncoded(true);
/*
* notify
*/
if (hasListeners())
dispatch(new ChunkEvent(this, ChunkEvent.Type.ENCODED));
}
/**
* note : non-locking
*
* @return
* @see org.jactr.core.chunk.IChunk#isEncoded()
*/
public boolean isEncoded()
{
return _chunkData.isEncoded();
}
/**
* note : non-locking
*
* @param ct
* @return
* @see org.jactr.core.chunk.IChunk#isA(org.jactr.core.chunktype.IChunkType)
*/
public boolean isA(IChunkType ct)
{
return getSymbolicChunk().isA(ct);
}
/**
* note : non-locking
*
* @param ct
* @return
* @see org.jactr.core.chunk.IChunk#isAStrict(org.jactr.core.chunktype.IChunkType)
*/
public boolean isAStrict(IChunkType ct)
{
return getSymbolicChunk().isAStrict(ct);
}
/**
* note : non-locking
*
* @param comment
* @see org.jactr.core.utils.Commentable#setComment(java.lang.String)
*/
public void setComment(String comment)
{
_chunkData.setComment(comment);
}
/**
* note : non-locking
*
* @return
* @see org.jactr.core.utils.Commentable#getComment()
*/
public String getComment()
{
return _chunkData.getComment();
}
/**
* note : non-locking
*
* @param key
* @return
* @see org.jactr.core.utils.IMetaContainer#getMetaData(java.lang.String)
*/
public Object getMetaData(String key)
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
return _chunkData.getMetaData().get(key);
}
/**
* @param key
* @param value
* @see org.jactr.core.utils.IMetaContainer#setMetaData(java.lang.String,
* java.lang.Object)
*/
public void setMetaData(String key, Object value)
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
try
{
getWriteLock().lock();
_chunkData.getMetaData().put(key, value);
}
finally
{
getWriteLock().unlock();
}
}
/**
*
* @return
* @see org.jactr.core.utils.IMetaContainer#getMetaDataKeys()
*/
public Collection<String> getMetaDataKeys()
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
try
{
getReadLock().lock();
return new ArrayList<String>(_chunkData.getMetaData().keySet());
}
finally
{
getReadLock().unlock();
}
}
/**
* note : non-locking
* @return
* @see org.jactr.core.chunk.IChunk#isMutable()
*/
public boolean isMutable()
{
return _chunkData.isMutable();
}
/**
* note : non-locking
* @param isMutable
* @see org.jactr.core.chunk.IChunk#setMutable(boolean)
*/
public void setMutable(boolean isMutable)
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
_chunkData.setMutable(isMutable);
}
/**
* @see org.jactr.core.chunk.IChunk#equalsSymbolic(org.jactr.core.chunk.IChunk)
*/
public boolean equalsSymbolic(IChunk chunk)
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
if (equals(chunk)) return true;
ISymbolicChunk mySC = getSymbolicChunk();
ISymbolicChunk oSC = chunk.getSymbolicChunk();
/*
* same chunktypes?
*/
if (!isAStrict(oSC.getChunkType())) return false;
/*
* same slots?
*/
Collection<ISlot> slots = mySC.getSlots(null);
for (ISlot slot : slots)
try
{
if (!oSC.getSlot(slot.getName()).equalValues(slot.getValue()))
return false;
}
catch (Exception e)
{
return false;
}
return true;
}
/**
* @return
* @see org.jactr.core.chunk.IChunk#getReadLock()
*/
final public Lock getReadLock()
{
return _chunkData.readLock();
}
/**
* @return
* @see org.jactr.core.chunk.IChunk#getWriteLock()
*/
final public Lock getWriteLock()
{
return _chunkData.writeLock();
}
}