/* * 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; import java.util.*; import org.jgap.impl.*; import org.jgap.util.*; /** * Abstract base class for all genes. Provides default implementations. * * @author Klaus Meffert * @since 2.2 */ public abstract class BaseGene implements Gene, IBusinessKey { /** String containing the CVS revision. Read out via reflection!*/ private final static String CVS_REVISION = "$Revision: 1.28 $"; /** * Constants for toString() */ public final static String S_APPLICATION_DATA = "Application data"; /** * Delta, useful for comparing doubles and floats. */ public static final double DELTA = 0.0000001; /** Energy of a gene, see RFE 1102206*/ private double m_energy; /** * Application-specific data that is attached to the Gene. This data may * assist the application in labelling this Gene. * JGAP ignores the data, aside from allowing it to be set and * retrieved and considering it in clone() and compareTo(). * * @since 2.4 */ private Object m_applicationData; /** * Method compareTo(): Should we also consider the application data when * comparing? Default is "false" as "true" means a Gene's losing its * identity when application data is set differently! * * @since 2.4 */ private boolean m_compareAppData; private Configuration m_configuration; /** * Unique ID of the gene that allows to distinct it from other genes. In the * best case, this ID is unique worldwide. */ private String m_uniqueID; /** * In case mutation, crossing over etc. happened, this sequence gives evidence * about the parent(s) of the current gene. */ private Map<Integer,String> m_uniqueIDTemplates; /** * * @param a_configuration the configuration to use * @throws InvalidConfigurationException * * @author Klaus Meffert * @since 3.0 */ public BaseGene(Configuration a_configuration) throws InvalidConfigurationException { if (a_configuration == null) { throw new InvalidConfigurationException("Configuration must not be null!"); } m_configuration = a_configuration; if (m_configuration.isUniqueKeysActive()) { m_uniqueIDTemplates = new HashMap(); IJGAPFactory factory = m_configuration.getJGAPFactory(); if (JGAPFactory.class.isAssignableFrom(factory.getClass())) { m_uniqueID = ( (JGAPFactory) (m_configuration.getJGAPFactory())). getUniqueKey(getClass().getName()); } } } /** * Retrieves the allele value represented by this Gene. * * @return the allele value of this Gene * @since 1.0 */ public Object getAllele() { return getInternalValue(); } /** * Retrieves the hash code value for a Gene. * Override if another hashCode() implementation is necessary or more * appropriate than this default implementation. * * @return this Gene's hash code * * @author Neil Rotstan * @author Klaus Meffert * @since 1.0 */ public int hashCode() { // If our internal value is null, then return zero. Otherwise, // just return the hash code of the allele Object. // ----------------------------------------------------------- if (getInternalValue() == null) { return -79; } else { return getInternalValue().hashCode(); } } /** * Executed by the genetic engine when this Gene instance is no * longer needed and should perform any necessary resource cleanup. * If you need a special cleanup, override this method. * * @author Klaus Meffert * @since 1.0 */ public void cleanup() { // No specific cleanup is necessary by default. // -------------------------------------------- } /** * Retrieves a string representation of this Gene's value that * may be useful for display purposes. * * @return a string representation of this Gene's value * * @author Klaus Meffert * @since 1.0 */ public String toString() { String representation; if (getInternalValue() == null) { representation = "null"; } else { representation = getInternalValue().toString(); } String appData; if (getApplicationData() != null) { appData = getApplicationData().toString(); } else { appData = "null"; } representation += ", " + S_APPLICATION_DATA + ":" + appData; return representation; } /** * @return the size of the gene, i.e the number of atomic elements. Always 1 * for non-composed Gene types. Override for composed Gene types * * @author Neil Rotstan * @since 1.0 */ public int size() { return 1; } /** * Compares this Gene with the given object and returns true if the other * object is a Gene of the same type and has the same value (allele) as * this Gene. Otherwise it returns false. * * @param a_other the object to compare to this Gene for equality * @return true if this Gene is equal to the given object, false otherwise * * @author Klaus Meffert * @since 1.1 */ public boolean equals(final Object a_other) { try { int result = compareTo(a_other); if (result == 0) { if (isCompareApplicationData()) { Gene otherGene = (Gene) a_other; int resultAppData = compareApplicationData(getApplicationData(), otherGene.getApplicationData()); return resultAppData == 0; } else { return true; } } else { return false; } } catch (ClassCastException e) { // If the other object isn't an Gene of current type // (like IntegerGene for IntegerGene's), then we're not equal. // ----------------------------------------------------------- return false; } } /** * Each Gene implementation holds its own m_value object keeping the allele * value. In your Gene implementation, just return it with this method * (see {@link org.jgap.impl.BooleanGene} for example) * @return the m_value object * * @author Klaus Meffert * @since 2.2 */ protected abstract Object getInternalValue(); /** * @return energy of the gene * * @author Klaus Meffert * @since 2.3 */ public double getEnergy() { return m_energy; } /** * Sets the energy of the gene * @param a_energy the energy to set * * @author Klaus Meffert * @since 2.3 */ public void setEnergy(final double a_energy) { m_energy = a_energy; } /** * This sets the application-specific data that is attached to this Gene. * Attaching application-specific data may be useful for * some applications when it comes time to distinguish a Gene from another. * JGAP ignores this data functionally. * * @param a_newData the new application-specific data to attach to this * Gene * * @author Klaus Meffert * @since 2.4 */ public void setApplicationData(final Object a_newData) { m_applicationData = a_newData; } /** * Retrieves the application-specific data that is attached to this Gene. * Attaching application-specific data may be useful for * some applications when it comes time to distinguish a Gene from another. * JGAP ignores this data functionally. * * @return the application-specific data previously attached to this Gene, * or null if there is no data attached * * @author Klaus Meffert * @since 2.4 */ public Object getApplicationData() { return m_applicationData; } /** * Should we also consider the application data when comparing? Default is * "false" as "true" means a Gene is losing its identity when * application data is set differently! * * @param a_doCompare true: consider application data in method compareTo * * @author Klaus Meffert * @since 2.4 */ public void setCompareApplicationData(final boolean a_doCompare) { m_compareAppData = a_doCompare; } /* * @return should we also consider the application data when comparing? * * @author Klaus Meffert * @since 2.4 */ public boolean isCompareApplicationData() { return m_compareAppData; } protected int compareApplicationData(final Object a_appdata1, final Object a_appdata2) { // Compare application data. // ------------------------- if (a_appdata1 == null) { if (a_appdata2 != null) { return -1; } else { return 0; } } else if (a_appdata2 == null) { return 1; } else { // The above code is contained in the following, but for performance // issues we keep it here redundantly. // ----------------------------------------------------------------- ICompareToHandler handler = getConfiguration().getJGAPFactory(). getCompareToHandlerFor(a_appdata1, a_appdata2.getClass()); if (handler != null) { try { return ( (Integer) handler.perform(a_appdata1, null, a_appdata2)). intValue(); } catch (Exception ex) { throw new Error(ex); } } else { return 0; } } } /** * Optional helper class for checking if a given allele value to be set * for a given gene is valid. If not, the allele value may not be set for the * gene or the gene type (e.g. IntegerGene) is not allowed in general! * * @since 2.5 (moved from CompositeGene, where it was since 2.0) */ private IGeneConstraintChecker m_geneAlleleChecker; /** * Sets the constraint checker to be used for this gene whenever method * setAllele(Object) is called. * @param a_constraintChecker the constraint checker to be set * * @author Klaus Meffert * @since 2.5 (moved from CompositeGene, where it was since 2.0) */ public void setConstraintChecker( final IGeneConstraintChecker a_constraintChecker) { m_geneAlleleChecker = a_constraintChecker; } /** * @return IGeneConstraintChecker the constraint checker to be used whenever * method setAllele(Object) is called. * * @author Klaus Meffert * @since 2.5 (moved from CompositeGene, where it was since 2.0) */ public IGeneConstraintChecker getConstraintChecker() { return m_geneAlleleChecker; } /** * Provides implementation-independent means for creating new Gene * instances. The new instance that is created and returned should be * setup with any implementation-dependent configuration that this Gene * instance is setup with (aside from the actual value, of course). For * example, if this Gene were setup with bounds on its value, then the * Gene instance returned from this method should also be setup with * those same bounds. This is important, as the JGAP core will invoke this * method on each Gene in the sample Chromosome in order to create each * new Gene in the same respective gene position for a new Chromosome. * * @return a new Gene instance of the same type and with the same setup as * this concrete Gene * * @author Neil Rostan * @author Klaus Meffert * @since 2.6 (since 1.0 in IntegerGene) */ public Gene newGene() { Gene result = newGeneInternal(); result.setConstraintChecker(getConstraintChecker()); result.setEnergy(getEnergy()); /**@todo clone app.data*/ result.setApplicationData(getApplicationData()); return result; } protected abstract Gene newGeneInternal(); /** * @return the configuration set * * @author Klaus Meffert * @since 3.0 */ public Configuration getConfiguration() { return m_configuration; } public String getBusinessKey() { Object allele = getAllele(); String result = getClass().getName() + PERSISTENT_FIELD_DELIMITER; if (allele == null) { return result; } return result + allele.toString(); } protected String encode(String a_string) { return StringKit.encode(a_string); } protected String decode(String a_string) { return StringKit.decode(a_string); } /** * @return unique ID of the gene, which allows to distinct this instance * from others, in the best case worldwide * * @author Klaus Meffert * @since 3.5 */ public String getUniqueID() { return m_uniqueID; } /** * A template is a gene that is the logical predecessor of the current * gene. A template can occur in mutation or crossing over. In the * latter case can be at least two template genes. This is why in this * setter method the parameter a_index exists. * * @param a_templateID the unique ID of the template * @param a_index the index of the template, e.g. in crossing over for the * second candidate gene this is 2 * * @author Klaus Meffert * @since 3.5 */ public void setUniqueIDTemplate(String a_templateID, int a_index) { m_uniqueIDTemplates.put(a_index, a_templateID); } /** * @param a_index the index of the template to retrieve the key for * @return String * * @author Klaus Meffert * @since 3.5 */ public String getUniqueIDTemplate(int a_index) { return m_uniqueIDTemplates.get(a_index); } }