/*
* This file is part of JGAP.
*
* JGAP offers a dual license model containing the LGPL as well as the MPL.
*
* For licensing information please see the file license.txt included with JGAP
* or have a look at the top of class org.jgap.Chromosome which representatively
* includes the JGAP license policy applicable for any file delivered with JGAP.
*/
package org.jgap.distr;
import java.io.*;
import java.util.*;
import org.apache.commons.lang.builder.*;
/**
* Represents a memory cell used within {@link org.jgap.distr.Culture}, a
* special form of memory.
* <P>
* CultureMemoryCell also stores metadata along with the value-to-store, like
* date/time of setting a value, change history.
*
* @author Klaus Meffert
* @since 2.3
*/
public class CultureMemoryCell
implements Serializable, Comparable {
/** String containing the CVS revision. Read out via reflection!*/
private final static String CVS_REVISION = "$Revision: 1.13 $";
/**
* Informative name of the memory cell (optional)
*/
private String m_name;
/**
* Version of the memory value, i.e. how many times has setValue(..) been
* called)? First version (no value assigned) is zero, second version (first
* time a value is assigned to the memory) is 1 etc.
*/
private int m_version;
/**
* Value of the memory cell
*/
private Object m_value;
/**
* How many times has the memory cell been read out?
*/
private int m_readAccessed;
/**
* How many historical values should be kept for evaluation purposes?
* Values less than one turn history feature off
*/
private int m_historySize;
private int m_internalHistorySize;
/**
* If history logging turned off, we need to keep the prior version for
* evaluation purpose, e.g. see getReadAccessedCurrentVersion
*/
private CultureMemoryCell m_previousVersion;
/**
* History of memory values
*/
private List m_history;
/**
* Time in milliseconds when the version number is incremented (e.g. when
* setting the value of the cell)
*/
private long m_dateTimeVersion;
/**
* Default constructor.
*
* @author Klaus Meffert
* @since 2.3
*/
public CultureMemoryCell() {
this(null);
}
/**
* Sets history size to 3.
* @param a_name informative name of the memory cell
*
* @author Klaus Meffert
* @since 2.3
*/
public CultureMemoryCell(final String a_name) {
this(a_name, 3);
}
/**
* Allows to freely specify informative name of memory cell as well as size
* of history to keep.
* @param a_name informative name of the memory cell
* @param a_historySize size of history to keep. Use values less than 1 for
* turning history logging off
*
* @author Klaus Meffert
* @since 2.3
*/
public CultureMemoryCell(final String a_name, final int a_historySize) {
setHistorySize(a_historySize);
m_history = new Vector(getHistorySize());
setName(a_name);
}
/**
* Sets the informative name of the memory cell.
* @param a_name informative name
*
* @author Klaus Meffert
* @since 2.3
*/
public void setName(final String a_name) {
m_name = a_name;
}
/**
* @return informative name of the memory cell
*
* @author Klaus Meffert
* @since 2.3
*/
public String getName() {
return m_name;
}
/**
* Sets a new memory value.
* @param a_value the memory value to set
*
* @author Klaus Meffert
* @since 2.3
*/
public void setValue(final Object a_value) {
if (m_historySize > 0) {
keepHistory(a_value, getVersion(), getName());
}
else {
m_previousVersion = getNewInstance(m_value, getVersion(),
getName());
}
m_value = a_value;
incrementVersion();
}
/**
* Convenience method to store a primitive double easily.
*
* @param a_value double value to store
*
* @author Klaus Meffert
* @since 2.3
*/
public void setDouble(final double a_value) {
setValue(new Double(a_value));
}
/**
* Convenience method to retrieve a primitive double value from memory
* easily. Here a ClassCastException could occur!
* @return double value representing current memory value
*
* @author Klaus Meffert
* @since 2.3
*/
public double getCurrentValueAsDouble() {
return ( (Double) getCurrentValue()).doubleValue();
}
/**
* @return current memory value
*
* @author Klaus Meffert
* @since 2.3
*/
public Object getCurrentValue() {
m_readAccessed++;
return m_value;
}
/**
* @return list of most recent entries (except current entry). The item at
* index 0 is the oldest, the item at highest index is the youngest one.
*
* @author Klaus Meffert
* @since 2.3
*/
public List getHistory() {
return m_history;
}
/**
* @return version of memory value, read only
*
* @author Klaus Meffert
* @since 2.3
*/
public int getVersion() {
return m_version;
}
/**
* Increment version number and keep track of current time.
*
* @author Klaus Meffert
* @since 2.3
*/
protected void incrementVersion() {
m_version++;
// Memorize current time.
m_dateTimeVersion = System.currentTimeMillis();
}
/**
* Puts an entry into history. Stores all information as a new
* CultureMemoryCell instance.
*
* @param a_value memory value to store
* @param a_version version of the value
* @param a_name name to store
*
* @author Klaus Meffert
* @since 2.3
*/
protected void keepHistory(final Object a_value, final int a_version,
final String a_name) {
trimHistory(m_historySize - 1);
// simply add a new instance of CultureMemoryCell for keeping history track
CultureMemoryCell cell = getNewInstance(a_value, a_version, a_name);
cell.m_internalHistorySize = m_historySize;
m_history.add(cell);
}
/**
* Creates a new instance of CultureMemoryCell preset with the given
* parameters. Used for creating history entries.
*
* @param a_value memory value to store
* @param a_version version of the value
* @param a_name name to store
* @return new instance of CultureMemoryCell
*
* @author Klaus Meffert
* @since 2.3
*/
protected CultureMemoryCell getNewInstance(final Object a_value,
final int a_version,
final String a_name) {
// DON'T USE SETTERS IN HERE BECAUSE OF ENDLESS LOOPS!
CultureMemoryCell cell = new CultureMemoryCell(a_name, 0);
cell.m_value = a_value;
cell.m_version = a_version;
return cell;
}
/**
* @return number of times the memory cell has been read accessed
*
* @author Klaus Meffert
* @since 2.3
*/
public int getReadAccessed() {
return m_readAccessed;
}
/**
* @return number of read accesses since current value has been set.
* Calculated by subtracting number of read accesses for prior version from
* total number of read accesses
*
* @author Klaus Meffert
* @since 2.3
*/
public int getReadAccessedCurrentVersion() {
if (m_historySize < 1) {
// Use internal (simple/atomic) history.
// -------------------------------------
return getReadAccessed() - m_previousVersion.getReadAccessed();
}
else {
// Use sophisticated history (list).
// ---------------------------------
CultureMemoryCell cell = (CultureMemoryCell) m_history.get(
m_history.size() - 1);
return getReadAccessed() - cell.getReadAccessed();
}
}
/**
* Sets the size of the history and scales down the history log it is larger
* than the given size.
* @param a_size new size of the history log = how many entries to store
*
* @author Klaus Meffert
* @since 2.3
*/
public void setHistorySize(final int a_size) {
if (getHistory() != null && a_size > getHistory().size()) {
trimHistory(a_size);
m_historySize = a_size;
}
else if (a_size < 0) {
m_historySize = 0;
}
else {
m_historySize = a_size;
}
}
/**
* Trims the history to the given size.
* @param a_size new size of history
*
* @author Klaus Meffert
* @since 2.3
*/
protected void trimHistory(final int a_size) {
// trim length of history
while (m_history.size() > a_size) {
// remove one entry (always the first one = oldest one)
m_history.remove(0);
}
}
/**
* @return size of the history
*
* @author Klaus Meffert
* @since 2.3
*/
public int getHistorySize() {
return m_historySize;
}
/**
* @return String representation of the cultural memory cell including all
* important information (also history log).
*
* @author Klaus Meffert
* @since 2.3
*/
public String toString() {
StringBuffer result = new StringBuffer();
toStringRecursive(result, getHistorySize());
return result.toString();
}
/**
* Recursive part of toString().
* @param a_result gathered result so far and modified here
* @param a_historySize history size just for information purpose
*
* @author Klaus Meffert
* @since 2.3
*/
protected void toStringRecursive(StringBuffer a_result,
final int a_historySize) {
List history = getHistory();
a_result.append("[Name:" + getName() + ";");
a_result.append("Value:" + m_value + ";"); //not getCurrentValue()!
a_result.append("Version:" + getVersion() + ";");
a_result.append("Read accessed:" + getReadAccessed() + ";");
a_result.append("History Size:" + a_historySize + ";");
a_result.append("History:[");
for (int i = 0; i < history.size(); i++) {
if (i > 0) {
a_result.append(";");
}
CultureMemoryCell cell = (CultureMemoryCell) history.get(i);
// do recursive call
cell.toStringRecursive(a_result, cell.m_internalHistorySize);
a_result.append("]");
}
a_result.append("]");
}
/**
* @return time in milliseconds when the current version has been created
*
* @author Klaus Meffert
* @since 3.0
*/
public long getVersionTimeMilliseconds() {
return m_dateTimeVersion;
}
/**
* The equals-method.
* @param a_other the other object to compare
* @return true if the objects are regarded as equal
*
* @author Klaus Meffert
* @since 3.0
*/
public boolean equals(Object a_other) {
try {
return compareTo(a_other) == 0;
} catch (ClassCastException cex) {
return false;
}
}
/**
* The compareTo-method.
* @param a_other the other object to compare
* @return -1, 0, 1
*
* @author Klaus Meffert
* @since 3.0
*/
public int compareTo(Object a_other) {
CultureMemoryCell other = (CultureMemoryCell) a_other;
if (other == null) {
return 1;
}
return new CompareToBuilder()
.append(m_value, other.m_value)
.append(m_version, other.m_version)
.append(m_historySize, other.m_historySize)
.toComparison();
}
}