Clover coverage report - JGAP 3.1
Coverage timestamp: Mo Dez 11 2006 21:16:18 CET
file stats: LOC: 681   Methods: 26
NCLOC: 336   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
CompositeGene.java 100% 98,8% 100% 99,3%
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.io.*;
 13    import java.net.*;
 14    import java.util.*;
 15    import java.lang.reflect.*;
 16    import org.jgap.*;
 17   
 18    /**
 19    * Ordered container for multiple genes
 20    * Has the same interface as a single gene and could be used accordingly.
 21    * Use the addGene(Gene) method to add single genes (possibly CompositeGenes)
 22    * after construction, an empty CompositeGene without genes makes no sense.
 23    * Beware that there are two equalities defined for a CompositeGene in respect
 24    * to its contained genes:
 25    * a) Two genes are (only) equal if they are identical
 26    * b) Two genes are (seen as) equal if their equals method returns true
 27    *
 28    * This influences several methods such as addGene. Notice that it is safer
 29    * to use addGene(a_gene, false) than addGene(a_gene, true) because the second
 30    * variant only allows to add genes not seen as equal to already added genes in
 31    * respect to their equals function. But: the equals function returns true for
 32    * two different DoubleGenes (e.g.) just after their creation. If no specific
 33    * (and hopefully different) allele is set for these DoubleGenes they are seen
 34    * as equal!
 35    *
 36    * @author Klaus Meffert
 37    * @author Audrius Meskauskas
 38    * @since 1.1
 39    */
 40    public class CompositeGene
 41    extends BaseGene
 42    implements ICompositeGene {
 43    /** String containing the CVS revision. Read out via reflection!*/
 44    private final static String CVS_REVISION = "$Revision: 1.52 $";
 45   
 46    /**
 47    * This field separates gene class name from
 48    * the gene persistent representation string.
 49    * '*' does not work properly with URLEncoder, so I have changed it to '#'
 50    */
 51    public final static String GENE_DELIMITER = "#";
 52   
 53    /**
 54    * Represents the heading delimiter that is used to separate genes in the
 55    * persistent representation of CompositeGene instances.
 56    */
 57    public final static String GENE_DELIMITER_HEADING = "<";
 58   
 59    /**
 60    * Represents the closing delimiter that is used to separate genes in the
 61    * persistent representation of CompositeGene instances.
 62    */
 63    public final static String GENE_DELIMITER_CLOSING = ">";
 64   
 65    private Gene m_geneTypeAllowed;
 66   
 67    /**
 68    * The genes contained in this CompositeGene
 69    *
 70    * @author Klaus Meffert
 71    * @since 1.1
 72    */
 73    private List m_genes;
 74   
 75    /**
 76    * Default constructor.<p>
 77    * Attention: The configuration used is the one set with the static method
 78    * Genotype.setConfiguration.
 79    * @throws InvalidConfigurationException
 80    *
 81    * @author Klaus Meffert
 82    * @since 1.1
 83    */
 84  1 public CompositeGene()
 85    throws InvalidConfigurationException {
 86  1 this(Genotype.getStaticConfiguration());
 87    }
 88   
 89    /**
 90    * @param a_config the configuration to use
 91    * @throws InvalidConfigurationException
 92    *
 93    * @author Klaus Meffert
 94    * @since 3.0
 95    */
 96  6616 public CompositeGene(Configuration a_config)
 97    throws InvalidConfigurationException {
 98  6616 this(a_config, null);
 99    }
 100   
 101    /**
 102    * Allows to specify which Gene implementation is allowed to be added to the
 103    * CompositeGene.
 104    *
 105    * @param a_config the configuration to use
 106    * @param a_geneTypeAllowed the class of Genes to be allowed to be added to
 107    * the CompositeGene
 108    * @throws InvalidConfigurationException
 109    *
 110    * @author Klaus Meffert
 111    * @since 2.0
 112    */
 113  6619 public CompositeGene(final Configuration a_config,
 114    final Gene a_geneTypeAllowed)
 115    throws InvalidConfigurationException {
 116  6619 super(a_config);
 117  6619 m_genes = new Vector();
 118  6619 if (a_geneTypeAllowed != null) {
 119  3 m_geneTypeAllowed = a_geneTypeAllowed;
 120    }
 121    }
 122   
 123    /**
 124    * Adds a gene to the CompositeGene
 125    * @param a_gene the gene to add
 126    */
 127  81 public void addGene(final Gene a_gene) {
 128  81 addGene(a_gene, false);
 129    }
 130   
 131    /**
 132    * @return the gene type allowed, or null if any type allowed
 133    *
 134    * @author Klaus Meffert
 135    * @since 2.4
 136    */
 137  2 public Gene getGeneTypeAllowed() {
 138  2 return m_geneTypeAllowed;
 139    }
 140   
 141    /**
 142    * Adds a gene to the CompositeGene's container. See comments in class
 143    * header for additional details about equality (concerning "strict" param.)
 144    * @param a_gene the gene to be added
 145    * @param a_strict false: add the given gene except the gene itself already is
 146    * contained within the CompositeGene's container. true: add the gene if
 147    * there is no other gene being equal to the given gene in request to the
 148    * Gene's equals method
 149    *
 150    * @author Klaus Meffert
 151    * @since 1.1
 152    */
 153  198 public void addGene(final Gene a_gene, final boolean a_strict) {
 154  198 if (a_gene == null) {
 155  1 throw new IllegalArgumentException("Gene instance must not be null!");
 156    }
 157  197 if (m_geneTypeAllowed != null) {
 158  2 if (!a_gene.getClass().getName().equals(m_geneTypeAllowed.getClass().
 159    getName())) {
 160  1 throw new IllegalArgumentException("Adding a "
 161    + a_gene.getClass().getName()
 162    + " has been forbidden!");
 163    }
 164    }
 165    // Check if gene already exists.
 166    // -----------------------------
 167  196 boolean containsGene;
 168  196 if (!a_strict) {
 169  190 containsGene = containsGeneByIdentity(a_gene);
 170    }
 171    else {
 172  6 containsGene = m_genes.contains(a_gene);
 173    }
 174  196 if (containsGene) {
 175  3 throw new IllegalArgumentException("The gene is already contained"
 176    + " in the CompositeGene!");
 177    }
 178  193 m_genes.add(a_gene);
 179    }
 180   
 181    /**
 182    * Removes the given gene from the collection of genes. The gene is only
 183    * removed if an object of the same identity is contained. The equals
 184    * method will not be used here intentionally
 185    * @param a_gene the gene to be removed
 186    * @return true: given gene found and removed
 187    *
 188    * @author Klaus Meffert
 189    * @since 1.1
 190    */
 191  5 public boolean removeGeneByIdentity(final Gene a_gene) {
 192  5 int size = size();
 193  5 if (size < 1) {
 194  2 return false;
 195    }
 196    else {
 197  3 for (int i = 0; i < size; i++) {
 198  4 if (geneAt(i) == a_gene) {
 199  1 m_genes.remove(i);
 200  1 return true;
 201    }
 202    }
 203    }
 204  2 return false;
 205    }
 206   
 207    /**
 208    * Removes the given gene from the collection of genes. The gene is
 209    * removed if another gene exists that is equal to the given gene in respect
 210    * to the equals method of the gene
 211    * @param a_gene the gene to be removed
 212    * @return true: given gene found and removed
 213    *
 214    * @author Klaus Meffert
 215    * @since 1.1
 216    */
 217  7 public boolean removeGene(final Gene a_gene) {
 218  7 return m_genes.remove(a_gene);
 219    }
 220   
 221    /**
 222    * Executed by the genetic engine when this Gene instance is no
 223    * longer needed and should perform any necessary resource cleanup.
 224    *
 225    * @author Klaus Meffert
 226    * @since 1.1
 227    */
 228  2 public void cleanup() {
 229  2 Gene gene;
 230  2 int size = m_genes.size();
 231  2 for (int i = 0; i < size; i++) {
 232  2 gene = (Gene) m_genes.get(i);
 233  2 gene.cleanup();
 234    }
 235    }
 236   
 237    /**
 238    * See interface Gene for description
 239    * @param a_numberGenerator the random number generator that should be used
 240    * to create any random values. It's important to use this generator to
 241    * maintain the user's flexibility to configure the genetic engine to use the
 242    * random number generator of their choice
 243    *
 244    * @author Klaus Meffert
 245    * @since 1.1
 246    */
 247  3 public void setToRandomValue(final RandomGenerator a_numberGenerator) {
 248  3 if (a_numberGenerator == null) {
 249  1 throw new IllegalArgumentException("Random generatoe must not be null!");
 250    }
 251  2 Gene gene;
 252  2 int size = m_genes.size();
 253  2 for (int i = 0; i < size; i++) {
 254  2 gene = (Gene) m_genes.get(i);
 255  2 gene.setToRandomValue(a_numberGenerator);
 256    }
 257    }
 258   
 259    /**
 260    * See interface Gene for description.
 261    * @param a_representation the string representation retrieved from a prior
 262    * call to the getPersistentRepresentation() method
 263    *
 264    * @throws UnsupportedRepresentationException
 265    *
 266    * @author Klaus Meffert
 267    * @author Audrius Meskauskas
 268    * @since 1.1
 269    */
 270  13 public void setValueFromPersistentRepresentation(String a_representation)
 271    throws UnsupportedRepresentationException {
 272  13 if (a_representation != null) {
 273  12 try {
 274    // Remove the old content.
 275    // -----------------------
 276  12 m_genes.clear();
 277  12 List r = split(a_representation);
 278  10 Iterator iter = r.iterator();
 279  10 StringTokenizer st;
 280  10 String clas;
 281  10 String representation;
 282  10 String g;
 283  10 Gene gene;
 284  10 while (iter.hasNext()) {
 285  38 g = URLDecoder.decode( (String) iter.next(), "UTF-8");
 286  38 st = new StringTokenizer(g, GENE_DELIMITER);
 287  38 if (st.countTokens() != 2)
 288  2 throw new UnsupportedRepresentationException("In " + g + ", " +
 289    "expecting two tokens, separated by " + GENE_DELIMITER);
 290  36 clas = st.nextToken();
 291  36 representation = st.nextToken();
 292  36 gene = createGene(clas, representation);
 293  35 addGene(gene);
 294    }
 295    }
 296    catch (Exception ex) {
 297  5 throw new UnsupportedRepresentationException(ex.toString());
 298    }
 299    }
 300    }
 301   
 302    /**
 303    * Creates a new instance of gene.
 304    * @param a_geneClassName name of the gene class
 305    * @param a_persistentRepresentation persistent representation of the gene to
 306    * create (could be obtained via getPersistentRepresentation)
 307    *
 308    * @return newly created gene
 309    * @throws Exception
 310    *
 311    * @author Klaus Meffert
 312    */
 313  36 protected Gene createGene(String a_geneClassName,
 314    String a_persistentRepresentation)
 315    throws Exception {
 316  36 Class geneClass = Class.forName(a_geneClassName);
 317  35 Constructor constr = geneClass.getConstructor(new Class[] {Configuration.class});
 318  35 Gene gene = (Gene) constr.newInstance(new Object[] {getConfiguration()});
 319  35 gene.setValueFromPersistentRepresentation(a_persistentRepresentation);
 320  35 return gene;
 321    }
 322   
 323    /**
 324    * See interface Gene for description
 325    * @return string representation of this Gene's current state
 326    * @throws UnsupportedOperationException
 327    *
 328    * @author Klaus Meffert
 329    * @author Audrius Meskauskas
 330    * @since 1.1
 331    */
 332  10 public String getPersistentRepresentation()
 333    throws UnsupportedOperationException {
 334  10 StringBuffer b = new StringBuffer();
 335  10 Iterator iter = m_genes.iterator();
 336  10 Gene gene;
 337  10 while (iter.hasNext()) {
 338  56 gene = (Gene) iter.next();
 339  56 b.append(GENE_DELIMITER_HEADING);
 340  56 try {
 341  56 b.append(
 342    URLEncoder.encode(
 343    gene.getClass().getName() +
 344    GENE_DELIMITER +
 345    gene.getPersistentRepresentation(), "UTF-8"
 346    ));
 347    }
 348    catch (UnsupportedEncodingException uex) {
 349  0 throw new RuntimeException("UTF-8 should always be supported!");
 350    }
 351  56 b.append(GENE_DELIMITER_CLOSING);
 352    }
 353  10 return b.toString();
 354    }
 355   
 356    /**
 357    * Retrieves the value represented by this Gene. All values returned
 358    * by this class will be Vector instances. Each element of the Vector
 359    * represents the allele of the corresponding gene in the CompositeGene's
 360    * container
 361    *
 362    * @return the value of this Gene
 363    *
 364    * @author Klaus Meffert
 365    * @since 1.1
 366    */
 367  2212 public Object getAllele() {
 368  2212 List alleles = new Vector();
 369  2212 Gene gene;
 370  2212 int size = m_genes.size();
 371  2212 for (int i = 0; i < size; i++) {
 372  29 gene = (Gene) m_genes.get(i);
 373  29 alleles.add(gene.getAllele());
 374    }
 375  2212 return alleles;
 376    }
 377   
 378    /**
 379    * Sets the value of the contained Genes to the new given value. This class
 380    * expects the value to be of a Vector type. Each element of the Vector
 381    * must conform with the type of the gene in the CompositeGene's container
 382    * at the corresponding position.
 383    *
 384    * @param a_newValue the new value of this Gene instance
 385    *
 386    * @author Klaus Meffert
 387    * @since 1.1
 388    */
 389  2215 public void setAllele(Object a_newValue) {
 390  2215 if (! (a_newValue instanceof List)) {
 391  3 throw new IllegalArgumentException(
 392    "The expected type of the allele"
 393    + " is a List descendent.");
 394    }
 395  2212 if (getConstraintChecker() != null) {
 396  2 if (!getConstraintChecker().verify(this, a_newValue, null, -1)) {
 397  1 return;
 398    }
 399    }
 400  2211 List alleles = (List) a_newValue;
 401  2211 Gene gene;
 402  2211 for (int i = 0; i < alleles.size(); i++) {
 403  26 gene = (Gene) m_genes.get(i);
 404  26 gene.setAllele(alleles.get(i));
 405    }
 406    }
 407   
 408    /**
 409    * Provides an implementation-independent means for creating new Gene
 410    * instances.
 411    *
 412    * @return a new Gene instance of the same type and with the same setup as
 413    * this concrete Gene
 414    *
 415    * @author Klaus Meffert
 416    * @since 1.1
 417    */
 418  2217 protected Gene newGeneInternal() {
 419  2217 try {
 420  2217 CompositeGene compositeGene = new CompositeGene(getConfiguration());
 421  2217 compositeGene.setConstraintChecker(getConstraintChecker());
 422  2217 Gene gene;
 423  2217 int geneSize = m_genes.size();
 424  2217 for (int i = 0; i < geneSize; i++) {
 425  39 gene = (Gene) m_genes.get(i);
 426  39 compositeGene.addGene(gene.newGene(), false);
 427    }
 428  2217 return compositeGene;
 429    }
 430    catch (InvalidConfigurationException iex) {
 431  0 throw new IllegalStateException(iex.getMessage());
 432    }
 433    }
 434   
 435    /**
 436    * Compares this CompositeGene with the specified object for order. A
 437    * false value is considered to be less than a true value. A null value
 438    * is considered to be less than any non-null value.
 439    *
 440    * @param a_other the CompositeGene to be compared
 441    * @return a negative integer, zero, or a positive integer as this object
 442    * is less than, equal to, or greater than the specified object
 443    *
 444    * @throws ClassCastException if the specified object's type prevents it
 445    * from being compared to this CompositeGene
 446    *
 447    * @author Klaus Meffert
 448    * @since 1.1
 449    */
 450  9939 public int compareTo(Object a_other) {
 451    // First, if the other gene (or its value) is null, then this is
 452    // the greater allele. Otherwise, just use the contained genes' compareTo
 453    // method to perform the comparison.
 454    // ---------------------------------------------------------------
 455  9939 if (a_other == null) {
 456  1 return 1;
 457    }
 458  9938 if (! (a_other instanceof CompositeGene)) {
 459  6485 return this.getClass().getName().compareTo(a_other.getClass().getName());
 460    }
 461  3453 CompositeGene otherCompositeGene = (CompositeGene) a_other;
 462  3453 if (otherCompositeGene.isEmpty()) {
 463    // If our value is also null, then we're the same. Otherwise,
 464    // this is the greater gene.
 465    // ----------------------------------------------------------
 466  3416 if (isEmpty()) {
 467  3415 return 0;
 468    }
 469    else {
 470  1 return 1;
 471    }
 472    }
 473    else {
 474    // Compare each gene against each other.
 475    // -------------------------------------
 476  37 int numberGenes = Math.min(size(), otherCompositeGene.size());
 477  37 Gene gene1;
 478  37 Gene gene2;
 479  37 for (int i = 0; i < numberGenes; i++) {
 480  51 gene1 = geneAt(i);
 481  51 gene2 = otherCompositeGene.geneAt(i);
 482  51 int result = gene1.compareTo(gene2);
 483  49 if (result != 0) {
 484  8 return result;
 485    }
 486    }
 487    // If everything is equal until now the CompositeGene with more
 488    // contained genes wins.
 489    // ------------------------------------------------------------
 490  27 if (size() == otherCompositeGene.size()) {
 491  22 if (isCompareApplicationData()) {
 492  8 return compareApplicationData(getApplicationData(),
 493    otherCompositeGene.getApplicationData());
 494    }
 495    else {
 496  14 return 0;
 497    }
 498    }
 499    else {
 500  5 if (size() > otherCompositeGene.size()) {
 501  2 return 1;
 502    }
 503    else {
 504  3 return -1;
 505    }
 506    }
 507    }
 508    }
 509   
 510    /**
 511    * Retrieves a string representation of this CompositeGene's value that
 512    * may be useful for display purposes.
 513    * @return string representation of this CompositeGene's value. Every
 514    * contained gene's string representation is delimited by the given
 515    * delimiter
 516    *
 517    * @author Neil Rotstan
 518    * @author Klaus Meffert
 519    * @author Audrius Meskauskas
 520    * @since 1.1
 521    */
 522  3 public String toString() {
 523  3 if (m_genes.isEmpty()) {
 524  1 return "CompositeGene=null";
 525    }
 526    else {
 527  2 String result = "CompositeGene=(";
 528  2 Gene gene;
 529  2 for (int i = 0; i < m_genes.size(); i++) {
 530  3 gene = (Gene) m_genes.get(i);
 531  3 result += gene;
 532  3 if (i < m_genes.size() - 1) {
 533  1 result += GENE_DELIMITER;
 534    }
 535    }
 536  2 return result + ")";
 537    }
 538    }
 539   
 540    /**
 541    * @return true: no genes contained, false otherwise
 542    *
 543    * @author Klaus Meffert
 544    * @since 1.1
 545    */
 546  6869 public boolean isEmpty() {
 547  6869 return m_genes.isEmpty() ? true : false;
 548    }
 549   
 550    /**
 551    * @param a_index index to return the gene at
 552    * @return the gene at the given index
 553    *
 554    * @author Klaus Meffert
 555    * @since 1.1
 556    */
 557  401 public Gene geneAt(int a_index) {
 558  401 return (Gene) m_genes.get(a_index);
 559    }
 560   
 561    /**
 562    * @return the number of genes contained
 563    *
 564    * @author Klaus Meffert
 565    * @since 1.1
 566    */
 567  8938 public int size() {
 568  8938 return m_genes.size();
 569    }
 570   
 571    /**
 572    * Checks whether a specific gene is already contained. The determination
 573    * will be done by checking for identity and not using the equal method!
 574    * @param gene the gene under test
 575    * @return true: the given gene object is contained
 576    *
 577    * @author Klaus Meffert
 578    * @since 1.1
 579    */
 580  190 public boolean containsGeneByIdentity(Gene gene) {
 581  190 boolean result;
 582  190 int size = size();
 583  190 if (size < 1) {
 584  97 result = false;
 585    }
 586    else {
 587  93 result = false;
 588  93 for (int i = 0; i < size; i++) {
 589    //check for identity
 590    //------------------
 591  249 if (geneAt(i) == gene) {
 592  2 result = true;
 593  2 break;
 594    }
 595    }
 596    }
 597  190 return result;
 598    }
 599   
 600    /**
 601    * Don't use this method, is makes no sense here. It is just there to
 602    * satisfy the Gene interface. Instead, loop over all contained genes and
 603    * call their applyMutation method.
 604    * @param a_index does not matter here
 605    * @param a_percentage does not matter here
 606    *
 607    * @author Klaus Meffert
 608    * @since 1.1
 609    */
 610  1 public void applyMutation(int a_index, double a_percentage) {
 611    // problem here: size() of CompositeGene not equal to (different)
 612    // sizes of contained genes.
 613    // Solution: Don't use CompositeGene.applyMutation, instead loop
 614    // over all contained genes and call their method
 615    // -------------------------------------------------------------
 616  1 throw new RuntimeException("applyMutation may not be called for "
 617    + "a CompositeGene. Call this method for each"
 618    + " gene contained in the CompositeGene.");
 619    }
 620   
 621    /**
 622    * Splits the input a_string into individual gene representations.
 623    * @param a_string the string to split
 624    * @return the elements of the returned array are the persistent
 625    * representation strings of the gene's components
 626    * @throws UnsupportedRepresentationException
 627    *
 628    * @author Audrius Meskauskas
 629    * @since 2.0
 630    */
 631  12 protected static final List split(String a_string)
 632    throws UnsupportedRepresentationException {
 633  12 List a = Collections.synchronizedList(new ArrayList());
 634  12 StringTokenizer st = new StringTokenizer
 635    (a_string, GENE_DELIMITER_HEADING + GENE_DELIMITER_CLOSING, true);
 636  12 while (st.hasMoreTokens()) {
 637  40 if (!st.nextToken().equals(GENE_DELIMITER_HEADING)) {
 638  1 throw new UnsupportedRepresentationException(a_string + " no open tag");
 639    }
 640  39 String n = st.nextToken();
 641  39 if (n.equals(GENE_DELIMITER_CLOSING)) {
 642  1 a.add(""); /* Empty token */
 643    }
 644    else {
 645  38 a.add(n);
 646  38 if (!st.nextToken().equals(GENE_DELIMITER_CLOSING)) {
 647  1 throw new UnsupportedRepresentationException
 648    (a_string + " no close tag");
 649    }
 650    }
 651    }
 652  10 return a;
 653    }
 654   
 655    /**
 656    * Retrieves the hash code value for this Gene.
 657    *
 658    * @return this Gene's hash code
 659    *
 660    * @author Klaus Meffert
 661    * @since 2.2
 662    */
 663  8555 public int hashCode() {
 664  8555 int hashCode = 1;
 665  8555 int geneHashcode;
 666  8555 for (int i = 0; i < size(); i++) {
 667  13 geneHashcode = geneAt(i).hashCode();
 668  13 hashCode = 31 * hashCode + geneHashcode;
 669    }
 670  8555 return hashCode;
 671    }
 672   
 673    /**
 674    * This method is not called internally because BaseGene.getAllele() is
 675    * overridden here!
 676    * @return always null
 677    */
 678  1 protected Object getInternalValue() {
 679  1 return null;
 680    }
 681    }