Clover coverage report - JGAP 3.1
Coverage timestamp: Mo Dez 11 2006 21:16:18 CET
file stats: LOC: 603   Methods: 19
NCLOC: 340   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
MapGene.java 81,6% 81,3% 89,5% 82%
coverage coverage
 1    /*
 2    * This file is part of JGAP.
 3    *
 4    * JGAP offers a dual license model containing the LGPL as well as the MPL.
 5    *
 6    * For licencing information please see the file license.txt included with JGAP
 7    * or have a look at the top of class org.jgap.Chromosome which representatively
 8    * includes the JGAP license policy applicable for any file delivered with JGAP.
 9    */
 10    package org.jgap.impl;
 11   
 12    import java.lang.reflect.*;
 13    import java.util.*;
 14    import org.jgap.*;
 15   
 16    /**
 17    * ATTENTION: This class is preliminary and subject of future adaptations! Use
 18    * with care or wait for a more mature version we are working on.
 19    * <p>
 20    * Creates a gene instance in which individual alleles have both a label (key)
 21    * and a value with a distinct meaning. For example, IntegerGene only allows
 22    * for values having a continuous range, and does not have a function where it
 23    * is possible to specify setValue...
 24    * <p>This implementation does not support specifying a range of valid
 25    * integer values. Instead it is planned to provide a constraint checker plugin
 26    * later on. With this, the current implementation will stay unchanged and can
 27    * be as performant as possible without losing flexibility.</p>
 28    *
 29    * @author Johnathan Kool, Organisation: RSMAS, University of Miami
 30    * @author Klaus Meffert (adaptations)
 31    * @since 2.4
 32    */
 33    public class MapGene
 34    extends BaseGene {
 35    /** String containing the CVS revision. Read out via reflection!*/
 36    private final static String CVS_REVISION = "$Revision: 1.20 $";
 37   
 38    /**
 39    * Container for valid alleles
 40    */
 41    private Map m_geneMap;
 42   
 43    /**
 44    * Represents the constant range of values supported by integers.
 45    */
 46    private Object m_value;
 47   
 48    /**
 49    * Represents the delimiter that is used to mark the allele map.
 50    */
 51    final static String ALLELEMAP_BEGIN_DELIMITER = "[";
 52   
 53    final static String ALLELEMAP_END_DELIMITER = "]";
 54   
 55    /**
 56    * Default constructor.<p>
 57    * Attention: The configuration used is the one set with the static method
 58    * Genotype.setConfiguration.
 59    * @throws InvalidConfigurationException
 60    *
 61    * @since 2.4
 62    */
 63  1 public MapGene()
 64    throws InvalidConfigurationException {
 65  1 this(Genotype.getStaticConfiguration());
 66    }
 67   
 68    /**
 69    * @param a_config the configuration to use
 70    * @throws InvalidConfigurationException
 71    *
 72    * @author Klaus Meffert
 73    * @since 3.0
 74    */
 75  41 public MapGene(final Configuration a_config)
 76    throws InvalidConfigurationException {
 77  41 super(a_config);
 78  41 m_geneMap = new HashMap();
 79    }
 80   
 81    /**
 82    * Constructor setting up valid alleles directly.
 83    * @param a_config the configuration to use
 84    * @param a_alleles the valid alleles of the gene
 85    * @throws InvalidConfigurationException
 86    *
 87    * @author Klaus Meffert
 88    * @since 2.4
 89    */
 90  14 public MapGene(final Configuration a_config, final Map a_alleles)
 91    throws InvalidConfigurationException {
 92  14 super(a_config);
 93  14 m_geneMap = new HashMap();
 94  14 addAlleles(a_alleles);
 95    }
 96   
 97  4 protected Gene newGeneInternal() {
 98  4 try {
 99  4 MapGene result = new MapGene(getConfiguration(), m_geneMap);
 100    // get m_value from original
 101  4 Object value = getAllele();
 102  4 result.setAllele(value);
 103  4 return result;
 104    }
 105    catch (InvalidConfigurationException iex) {
 106  0 throw new IllegalStateException(iex.getMessage());
 107    }
 108    }
 109   
 110    /**
 111    * Adds a potential allele value to the collection.
 112    *
 113    * @param a_key the key to be added
 114    * @param a_value the Integer value to be added
 115    * @since 2.4
 116    */
 117  170 public void addAllele(final Object a_key, final Object a_value) {
 118  170 m_geneMap.put(a_key, a_value);
 119    }
 120   
 121    /**
 122    * Adds a potential allele value to the collection.
 123    *
 124    * @param a_value the value to be added, also used as key
 125    *
 126    * @author Klaus Meffert
 127    * @since 2.4
 128    */
 129  1 public void addAllele(final Object a_value) {
 130  1 m_geneMap.put(a_value, a_value);
 131    }
 132   
 133    /**
 134    * Convenience method for addAllele (Object's that are Integer's)
 135    *
 136    * @param a_value the int value to be added, also used as key
 137    *
 138    * @author Klaus Meffert
 139    * @since 2.4
 140    */
 141  4 public void addAllele(final int a_value) {
 142  4 m_geneMap.put(new Integer(a_value), new Integer(a_value));
 143    }
 144   
 145    /**
 146    * Add a set of potential allele values to the collection
 147    *
 148    * @param a_alleles the set of alleles to be added
 149    *
 150    * @since 2.4
 151    */
 152  14 public void addAlleles(final Map a_alleles) {
 153  14 if (a_alleles == null) {
 154  1 throw new IllegalArgumentException("List of alleles may not be null!");
 155    }
 156    else {
 157  13 m_geneMap.putAll(a_alleles);
 158    }
 159    }
 160   
 161    /**
 162    * Removes a potential allele or set of alleles from the collection.
 163    *
 164    * @param a_key the unique value(s) of the object(s) to be removed
 165    *
 166    * @since 2.4
 167    */
 168  2 public void removeAlleles(final Object a_key) {
 169  2 m_geneMap.remove(a_key);
 170    }
 171   
 172    /**
 173    * @return the map of alleles
 174    *
 175    * @author Klaus Meffert
 176    * @since 3.0
 177    */
 178  6 public Map getAlleles() {
 179  6 return m_geneMap;
 180    }
 181   
 182    /**
 183    * Sets the allele value to be a random value using a defined random number
 184    * generator. If no valid alleles are defined, any allele is allowed. Then,
 185    * a new Integer with random value is set as random value. Override this
 186    * method if you want a different behaviour, such as a Double instead of the
 187    * Integer type.
 188    *
 189    * @param a_numberGenerator the random generator to use
 190    *
 191    * @author Klaus Meffert
 192    * @since 2.4
 193    */
 194  2 public void setToRandomValue(final RandomGenerator a_numberGenerator) {
 195  2 if (m_geneMap.isEmpty()) {
 196  1 m_value = new Integer(a_numberGenerator.nextInt());
 197    }
 198    else {
 199  1 m_value = m_geneMap.get(m_geneMap.keySet().toArray()[a_numberGenerator.
 200    nextInt(m_geneMap.size())]);
 201    }
 202    }
 203   
 204    /**
 205    * See interface Gene for description of applyMutation.
 206    *
 207    * For this kind of gene, providing an index and a percentage of mutation
 208    * would have no significance because the individual allele forms are
 209    * independent of one another. In mutating, they can only change from one
 210    * form to another. It may be possible to weight the likelihood of mutation
 211    * to different forms, but that is not implemented currently.
 212    *
 213    * @param a_index ignored here
 214    * @param a_percentage ignored here
 215    *
 216    * @author Klaus Meffert
 217    * @author Johnathan Kool
 218    * @since 2.4
 219    */
 220  0 public void applyMutation(final int a_index, final double a_percentage) {
 221  0 RandomGenerator rn;
 222  0 rn = getConfiguration().getRandomGenerator();
 223  0 setToRandomValue(rn);
 224    }
 225   
 226    /**
 227    * Sets the value and internal state of this Gene from the string
 228    * representation returned by a previous invocation of the
 229    * getPersistentRepresentation() method. This is an optional method but,
 230    * if not implemented, XML persistence and possibly other features will not
 231    * be available. An UnsupportedOperationException should be thrown if no
 232    * implementation is provided.
 233    *
 234    * @param a_representation the string representation retrieved from a prior
 235    * call to the getPersistentRepresentation() method.
 236    *
 237    * @throws UnsupportedOperationException to indicate that no implementation
 238    * is provided for this method
 239    * @throws UnsupportedRepresentationException if this Gene implementation
 240    * does not support the given string representation.
 241    *
 242    * @author Neil Rostan
 243    * @author Klaus Meffert
 244    * @since 2.4
 245    */
 246  9 public void setValueFromPersistentRepresentation(final String
 247    a_representation)
 248    throws UnsupportedRepresentationException {
 249  9 if (a_representation != null) {
 250  8 StringTokenizer tokenizer = new StringTokenizer(a_representation,
 251    PERSISTENT_FIELD_DELIMITER);
 252    // Make sure the representation contains the correct number of
 253    // fields. If not, throw an exception.
 254    // -----------------------------------------------------------
 255  8 if (tokenizer.countTokens() != 2) {
 256  0 throw new UnsupportedRepresentationException(
 257    "The format of the given persistent representation " +
 258    "is not recognized: it must contain two tokens.");
 259    }
 260  8 String valueRepresentation = tokenizer.nextToken();
 261    // First parse and set the representation of the value.
 262    // ----------------------------------------------------
 263  8 if (valueRepresentation.equals("null")) {
 264  3 m_value = null;
 265    }
 266    else {
 267  5 try {
 268  5 m_value = new Integer(Integer.parseInt(valueRepresentation));
 269    }
 270    catch (NumberFormatException e) {
 271  0 throw new UnsupportedRepresentationException(
 272    "The format of the given persistent representation " +
 273    "is not recognized: field 1 does not appear to be " +
 274    "an integer value.");
 275    }
 276    }
 277    // Parse gene map.
 278    // ---------------
 279  8 String s = tokenizer.nextToken();
 280  8 tokenizer = new StringTokenizer(s, ",");
 281  8 int lastWasOpening = 0;
 282  8 String key = null;
 283  8 String keyClass = null;
 284  8 String valueClass = null;
 285  8 while (tokenizer.hasMoreTokens()) {
 286  591 String element = tokenizer.nextToken(",");
 287  591 if (lastWasOpening == 1) {
 288  148 key = element.substring(0);
 289  148 lastWasOpening = 2;
 290    }
 291  443 else if (lastWasOpening == 2) {
 292  148 valueClass = element.substring(0);
 293  148 lastWasOpening = 3;
 294    }
 295  295 else if (lastWasOpening == 3) {
 296  147 if (element.endsWith(")")) {
 297  147 element = element.substring(0, element.length() - 1);
 298  147 try {
 299  147 Class keyType = Class.forName(keyClass);
 300  147 Constructor keyC = keyType.getConstructor(new Class[]{String.class});
 301  147 Object keyObject = keyC.newInstance(new Object[]{key});
 302   
 303  147 Class valueType = Class.forName(valueClass);
 304  147 Constructor valueC = valueType.getConstructor(new Class[]{String.class});
 305  147 Object valueObject = valueC.newInstance(new Object[]{element});
 306  147 addAllele(keyObject, valueObject);
 307  147 lastWasOpening = 0;
 308    } catch (Exception cex) {
 309  0 throw new UnsupportedRepresentationException("Invalid class: "
 310    + keyClass);
 311    }
 312    }
 313    else {
 314  0 throw new IllegalStateException("Closing bracket missing");
 315    }
 316    }
 317    else {
 318  148 if (element.startsWith("(")) {
 319  148 keyClass = element.substring(1);
 320  148 lastWasOpening = 1;
 321    }
 322    else {
 323  0 throw new IllegalStateException("Opening bracket missing");
 324    }
 325    }
 326    }
 327  8 if (lastWasOpening != 0) {
 328  1 throw new IllegalStateException("Elements missing");
 329    }
 330    }
 331    }
 332   
 333    /**
 334    * Retrieves a string representation of this Gene that includes any
 335    * information required to reconstruct it at a later time, such as its
 336    * value and internal state. This string will be used to represent this
 337    * Gene in XML persistence. This is an optional method but, if not
 338    * implemented, XML persistence and possibly other features will not be
 339    * available. An UnsupportedOperationException should be thrown if no
 340    * implementation is provided.
 341    *
 342    * @return string representation of this Gene's current state
 343    * @throws UnsupportedOperationException to indicate that no implementation
 344    * is provided for this method
 345    *
 346    * @author Neil Rostan
 347    * @author Klaus Meffert
 348    * @since 2.4
 349    */
 350  4 public String getPersistentRepresentation()
 351    throws UnsupportedOperationException {
 352    // The persistent representation includes the value and the allele
 353    // assignment.
 354    // ---------------------------------------------------------------
 355  4 Iterator it = m_geneMap.keySet().iterator();
 356  4 StringBuffer strbf = new StringBuffer();
 357  4 boolean first = true;
 358  4 while (it.hasNext()) {
 359  140 if (!first) {
 360  136 strbf.append(",");
 361    }
 362  140 Object key = it.next();
 363  140 Object value = m_geneMap.get(key);
 364  140 strbf.append("(" + key.getClass().getName() + "," + key.toString() + "," +
 365    value.getClass().getName() + "," + value.toString() + ")");
 366  140 first = false;
 367    }
 368  4 return m_value.toString() + MapGene.PERSISTENT_FIELD_DELIMITER +
 369    strbf.toString();
 370    }
 371   
 372    /**
 373    * Sets the value (allele) of this Gene to the new given value. This class
 374    * expects the value to be an instance of current type (e.g. Integer).
 375    *
 376    * @param a_newValue the new value of this Gene instance
 377    *
 378    * @author Johnathan Kool
 379    * @since 2.4
 380    */
 381  21 public void setAllele(Object a_newValue) {
 382    // ignore null value as it should have no effect here (otherwise problematic
 383    // in conjunction with newGene)
 384  21 if (a_newValue == null) {
 385  3 return;
 386    }
 387  18 if (m_geneMap.keySet().isEmpty()) {
 388  11 m_value = a_newValue;
 389    }
 390  7 else if (m_geneMap.keySet().contains(a_newValue)) {
 391  6 m_value = m_geneMap.get(a_newValue);
 392    }
 393    else {
 394  1 throw new IllegalArgumentException("Allele value being set ("
 395    + a_newValue
 396    + ") is not an element of the set of"
 397    + " permitted values.");
 398    }
 399    }
 400   
 401    /**
 402    * Compares this NumberGene with the specified object (which must also
 403    * be a NumberGene) for order, which is determined by the number
 404    * value of this Gene compared to the one provided for comparison.
 405    *
 406    * @param a_other the NumberGene to be compared to this NumberGene
 407    * @return a negative integer, zero, or a positive integer as this object
 408    * is less than, equal to, or greater than the object provided for comparison
 409    *
 410    * @throws ClassCastException if the specified object's type prevents it from
 411    * being compared to this Gene
 412    *
 413    * @author Klaus Meffert
 414    * @author Johnathan Kool
 415    * @since 2.4
 416    */
 417  24 public int compareTo(Object a_other) {
 418  24 MapGene otherGene = (MapGene) a_other;
 419    // First, if the other gene (or its value) is null, then this is
 420    // the greater allele. Otherwise, just use the overridden compareToNative
 421    // method to perform the comparison.
 422    // ---------------------------------------------------------------
 423  21 if (otherGene == null) {
 424  1 return 1;
 425    }
 426  20 else if (otherGene.m_value == null) {
 427    // If our value is not null, then we're the greater gene.
 428    // ------------------------------------------------------
 429  14 if (m_value != null) {
 430  0 return 1;
 431    }
 432    }
 433  20 try {
 434  20 int size1 = m_geneMap.size();
 435  20 int size2 = otherGene.m_geneMap.size();
 436  20 if (size1 != size2) {
 437  2 if (size1 < size2) {
 438  1 return -1;
 439    }
 440    else {
 441  1 return 1;
 442    }
 443    }
 444    else {
 445    // Compare geneMap keys and values.
 446  18 Iterator it1 = m_geneMap.keySet().iterator();
 447    // Iterator it2 = otherGene.m_geneMap.keySet().iterator();
 448  18 while (it1.hasNext()) {
 449  190 Object key1 = it1.next();
 450  190 if (!otherGene.m_geneMap.keySet().contains(key1)) {
 451  2 Object key2 = otherGene.m_geneMap.keySet().iterator().next();
 452  2 if (Comparable.class.isAssignableFrom(key1.getClass())
 453    && Comparable.class.isAssignableFrom(key2.getClass())) {
 454  2 return ( (Comparable) key1).compareTo(key2);
 455    }
 456    else {
 457    // Arbitrarily return -1
 458  0 return -1;
 459    }
 460    }
 461  188 Object value1 = m_geneMap.get(key1);
 462  188 Object value2 = otherGene.m_geneMap.get(key1);
 463  188 if (value1 == null && value2 != null) {
 464  1 return -1;
 465    }
 466  187 else if (value1 == null && value2 != null) {
 467  0 return -1;
 468    }
 469  187 else if (!value1.equals(value2)) {
 470  1 if (value2 == null) {
 471  1 return 1;
 472    }
 473    else {
 474  0 if (Comparable.class.isAssignableFrom(value1.getClass())
 475    && Comparable.class.isAssignableFrom(value2.getClass())) {
 476  0 return ( (Comparable) value1).compareTo(value2);
 477    }
 478    else {
 479    // Arbitrarily return -1
 480  0 return -1;
 481    }
 482    }
 483    }
 484    }
 485    }
 486  14 if (m_value == null) {
 487  8 if (otherGene.m_value != null) {
 488  0 return 1;
 489    }
 490    else {
 491  8 return 0;
 492    }
 493    }
 494  6 Method method = m_value.getClass().getMethod("compareTo",
 495    new Class[] {otherGene.m_value.getClass()});
 496  6 Integer i = (Integer) method.invoke(m_value,
 497    new Object[] {otherGene.m_value});
 498  6 return i.intValue();
 499    }
 500    catch (InvocationTargetException ex) {
 501  0 ex.printStackTrace();
 502  0 throw new IllegalArgumentException("CompareTo method of the Gene value" +
 503    " object cannot be invoked.");
 504    }
 505    catch (IllegalArgumentException ex) {
 506  0 ex.printStackTrace();
 507  0 throw new IllegalArgumentException("The value object of the Gene does" +
 508    " not have a compareTo method. It" +
 509    " cannot be compared.");
 510    }
 511    catch (IllegalAccessException ex) {
 512  0 ex.printStackTrace();
 513  0 throw new IllegalArgumentException("The compareTo method of the Gene" +
 514    " value object cannot be accessed ");
 515    }
 516    catch (SecurityException ex) {
 517  0 ex.printStackTrace();
 518  0 throw new IllegalArgumentException("The compareTo method of the Gene" +
 519    " value object cannot be accessed." +
 520    " Insufficient permission levels.");
 521    }
 522    catch (NoSuchMethodException ex) {
 523  0 ex.printStackTrace();
 524  0 throw new IllegalArgumentException("The value object of the Gene does" +
 525    " not have a compareTo method. It" +
 526    " cannot be compared.");
 527    }
 528    }
 529   
 530    /**
 531    * @return the internal value of the gene
 532    * @since 2.4
 533    */
 534  11 protected Object getInternalValue() {
 535  11 return m_value;
 536    }
 537   
 538    /**
 539    * Modified hashCode() function to return different hashcodes for differently
 540    * ordered genes in a chromosome
 541    * @return -1 if no allele set, otherwise value return by BaseGene.hashCode()
 542    *
 543    * @author Klaus Meffert
 544    * @since 2.4
 545    */
 546  0 public int hashCode() {
 547  0 if (getInternalValue() == null) {
 548  0 return -71;
 549    }
 550    else {
 551  0 return super.hashCode();
 552    }
 553    }
 554   
 555    /**
 556    * Retrieves a string representation of this Gene's value that may be useful
 557    * for display purposes.
 558    *
 559    * @return a string representation of this Gene's value
 560    *
 561    * @author Klaus Meffert
 562    * @since 2.4
 563    */
 564  6 public String toString() {
 565  6 String result = "[";
 566  6 if (m_geneMap.size() < 1) {
 567  1 result += "null";
 568    }
 569    else {
 570  5 Set keys = m_geneMap.keySet();
 571  5 Iterator keyIterator = keys.iterator();
 572  5 boolean firstTime = true;
 573  5 while (keyIterator.hasNext()) {
 574  9 if (!firstTime) {
 575  4 result += ",";
 576    }
 577    else {
 578  5 firstTime = false;
 579    }
 580  9 Object key = keyIterator.next();
 581  9 String keyString;
 582  9 if (key == null) {
 583  0 keyString = "null";
 584    }
 585    else {
 586  9 keyString = key.toString();
 587    }
 588  9 result += "(" + keyString + ",";
 589  9 Object value = m_geneMap.get(key);
 590  9 String valueString;
 591  9 if (value == null) {
 592  0 valueString = "null";
 593    }
 594    else {
 595  9 valueString = value.toString();
 596    }
 597  9 result += valueString + ")";
 598    }
 599    }
 600  6 result += "]";
 601  6 return result;
 602    }
 603    }