/*
* 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.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.Lock;
import javolution.util.FastList;
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;
import org.jactr.core.utils.DefaultAdaptable;
/**
* abstract chunk that handles most common logic for the developer.
*
* @author harrison
*/
public class DefaultChunk extends DefaultAdaptable 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 #mergeInto(IChunk)}
* so we do not want the value to be cached.
*/
private volatile ChunkData _chunkData;
protected ISubsymbolicChunk _subsymbolicChunk;
protected ISymbolicChunk _symbolicChunk;
public DefaultChunk(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)
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
if (getSymbolicChunk() != null && getSubsymbolicChunk() != null)
_chunkData.getChunkDispatcher().fire(chunkEvent);
}
/**
* @param pEvent
* @see org.jactr.core.chunk.IChunk#dispatch(org.jactr.core.event.ParameterEvent)
*/
public void dispatch(ParameterEvent pEvent)
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
if (getSymbolicChunk() != null && getSubsymbolicChunk() != null)
_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);
}
public void bind(ISymbolicChunk symbolicChunk,
ISubsymbolicChunk subsymbolicChunk)
{
_symbolicChunk = symbolicChunk;
_subsymbolicChunk = subsymbolicChunk;
}
public ISubsymbolicChunk getSubsymbolicChunk()
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
return _subsymbolicChunk;
}
public ISymbolicChunk getSymbolicChunk()
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
return _symbolicChunk;
}
/**
* merge the metadata, this does not affect the symbolic/subsymbolic contents.
* That needs to be handled by the DM so that the original sub/symbolic
* contents can be disposed of properly
*
* @param masterChunk
*/
public void mergeInto(IChunk masterChunk)
{
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
Lock originalWriteLock = getWriteLock();
try
{
originalWriteLock.lock();
if (masterChunk instanceof DefaultChunk)
{
DefaultChunk mc = (DefaultChunk) masterChunk;
_chunkData.dispose();
_chunkData = mc._chunkData;
// adopt the adapter functionality
adopt(mc);
}
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()
{
Lock lock = getWriteLock();
try
{
lock.lock();
if (hasBeenDisposed())
throw new IllegalChunkStateException("Cant dispose of " + this
+ ", already been disposed!");
_chunkData.dispose();
}
finally
{
lock.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)
{
Lock lock = getWriteLock();
try
{
lock.lock();
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);
}
finally
{
lock.unlock();
}
/*
* 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.ICommentable#setComment(java.lang.String)
*/
public void setComment(String comment)
{
_chunkData.setComment(comment);
}
/**
* note : non-locking
*
* @return
* @see org.jactr.core.utils.ICommentable#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() || _chunkData == null)
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)
{
Lock lock = getWriteLock();
try
{
lock.lock();
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
Map<String, Object> meta = _chunkData.getMetaData();
meta.put(key, value);
}
finally
{
lock.unlock();
}
}
/**
* @return
* @see org.jactr.core.utils.IMetaContainer#getMetaDataKeys()
*/
public Collection<String> getMetaDataKeys()
{
Lock lock = getReadLock();
try
{
lock.lock();
if (hasBeenDisposed())
throw new IllegalChunkStateException(this + " has been disposed!");
return new ArrayList<String>(_chunkData.getMetaData().keySet());
}
finally
{
lock.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?
*/
FastList<ISlot> slots = FastList.newInstance();
try
{
for (ISlot slot : mySC.getSlots(slots))
try
{
if (!oSC.getSlot(slot.getName()).equalValues(slot.getValue()))
return false;
}
catch (Exception e)
{
return false;
}
return true;
}
finally
{
FastList.recycle(slots);
}
}
/**
* @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();
}
@Override
public Object getAdapter(Class adapterClass)
{
Object superRtn = super.getAdapter(adapterClass);
if (superRtn != null) return superRtn;
if (ISymbolicChunk.class.equals(adapterClass))
return getSymbolicChunk();
else if (ISubsymbolicChunk.class.equals(adapterClass))
return getSubsymbolicChunk();
else if (ISymbolicChunk.class.isAssignableFrom(adapterClass))
return getSymbolicChunk().getAdapter(adapterClass);
else if (ISubsymbolicChunk.class.isAssignableFrom(adapterClass))
return getSubsymbolicChunk().getAdapter(adapterClass);
else
{
// test to see if our sub or sym implement
Class clazz = getSymbolicChunk().getClass();
if (adapterClass.isAssignableFrom(clazz)) return getSymbolicChunk();
clazz = getSubsymbolicChunk().getClass();
if (adapterClass.isAssignableFrom(clazz)) return getSubsymbolicChunk();
}
return null;
}
@Override
public String toString()
{
if (_symbolicChunk == null) return super.toString();
return _symbolicChunk.getName();
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result
+ (_subsymbolicChunk == null ? 0 : _subsymbolicChunk.hashCode());
result = prime * result
+ (_symbolicChunk == null ? 0 : _symbolicChunk.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof DefaultChunk)) return false;
final DefaultChunk other = (DefaultChunk) obj;
if (_subsymbolicChunk == null)
{
if (other._subsymbolicChunk != null) return false;
}
else if (!_subsymbolicChunk.equals(other._subsymbolicChunk)) return false;
if (_symbolicChunk == null)
{
if (other._symbolicChunk != null) return false;
}
else if (!_symbolicChunk.equals(other._symbolicChunk)) return false;
return true;
}
}