Clover coverage report - JGAP 3.1
Coverage timestamp: Mo Dez 11 2006 21:16:18 CET
file stats: LOC: 628   Methods: 22
NCLOC: 350   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
StringGene.java 100% 98,1% 100% 98,8%
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 org.jgap.*;
 16   
 17    /**
 18    * A Gene implementation that supports a string for its allele. The valid
 19    * alphabet as well as the minimum and maximum length of the string can be
 20    * specified.<p>
 21    * An alphabet == null indicates that all characters are seen as valid.<br>
 22    * An alphabet == "" indicates that no character is seen to be valid.<p>
 23    * Partly copied from IntegerGene.
 24    *
 25    * @author Klaus Meffert
 26    * @author Audrius Meskauskas
 27    * @since 1.1
 28    */
 29    public class StringGene
 30    extends BaseGene {
 31    //Constants for ready-to-use alphabets or serving as part of concetenation
 32    public static final String ALPHABET_CHARACTERS_UPPER =
 33    "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 34   
 35    public static final String ALPHABET_CHARACTERS_LOWER =
 36    "abcdefghijklmnopqrstuvwxyz";
 37   
 38    public static final String ALPHABET_CHARACTERS_DIGITS = "0123456789";
 39   
 40    public static final String ALPHABET_CHARACTERS_SPECIAL = "+.*/\\,;@";
 41   
 42    /** String containing the CVS revision. Read out via reflection!*/
 43    private final static String CVS_REVISION = "$Revision: 1.54 $";
 44   
 45    private int m_minLength;
 46   
 47    private int m_maxLength;
 48   
 49    private String m_alphabet;
 50   
 51    /**
 52    * References the internal String value (allele) of this Gene.
 53    */
 54    private String m_value;
 55   
 56    /**
 57    * Default constructor, sets minimum and maximum length to arbitrary.
 58    * You need to set the valid alphabet later!<p>
 59    * Do not use this constructor with a sample chromosome set in the
 60    * configuration.<p>
 61    * Attention: The configuration used is the one set with the static method
 62    * Genotype.setConfiguration.
 63    * @throws InvalidConfigurationException
 64    *
 65    * @author Klaus Meffert
 66    * @since 1.1
 67    */
 68  1 public StringGene()
 69    throws InvalidConfigurationException {
 70  1 this(Genotype.getStaticConfiguration());
 71    }
 72   
 73    /**
 74    * Default constructor, sets minimum and maximum length to arbitrary.
 75    * You need to set the valid alphabet later!<p>
 76    * Do not use this constructor with a sample chromosome set in the
 77    * configuration.
 78    * @param a_config the configuration to use
 79    * @throws InvalidConfigurationException
 80    *
 81    * @author Klaus Meffert
 82    * @since 3.0
 83    */
 84  4350 public StringGene(final Configuration a_config)
 85    throws InvalidConfigurationException {
 86  4350 this(a_config, 0, 0);
 87    }
 88   
 89    /**
 90    * Constructor, allows to specify minimum and maximum lengths of the string
 91    * held by this gene. You need to set the valid alphabet later!<p>
 92    * Do not use this constructor with a sample chromosome set in the
 93    * configuration.
 94    * @param a_config the configuration to use
 95    * @param a_minLength minimum valid length of allele
 96    * @param a_maxLength maximum valid length of allele
 97    * @throws InvalidConfigurationException
 98    *
 99    * @author Klaus Meffert
 100    * @since 1.1
 101    */
 102  4393 public StringGene(final Configuration a_config, final int a_minLength,
 103    final int a_maxLength)
 104    throws InvalidConfigurationException {
 105  4393 this(a_config, a_minLength, a_maxLength, null);
 106    }
 107   
 108    /**
 109    * Constructor, allows to specify minimum and maximum lengths of the string
 110    * held by this gene, as well as the valid alphabet. This constructor can be
 111    * used to construct a sample chromosome with a configuration.
 112    *
 113    * @param a_config the configuration to use
 114    * @param a_minLength minimum valid length of an allele
 115    * @param a_maxLength maximum valid length of an allele
 116    * @param a_alphabet valid alphabet for an allele
 117    * @throws InvalidConfigurationException
 118    *
 119    * @author Klaus Meffert
 120    * @since 2.0
 121    */
 122  7904 public StringGene(final Configuration a_config, final int a_minLength,
 123    final int a_maxLength, final String a_alphabet)
 124    throws InvalidConfigurationException {
 125  7904 super(a_config);
 126  7904 if (a_minLength < 0) {
 127  1 throw new IllegalArgumentException(
 128    "minimum length must be greater than"
 129    + " zero!");
 130    }
 131  7903 if (a_maxLength < a_minLength) {
 132  1 throw new IllegalArgumentException(
 133    "minimum length must be smaller than"
 134    + " or equal to maximum length!");
 135    }
 136  7902 m_minLength = a_minLength;
 137  7902 m_maxLength = a_maxLength;
 138  7902 setAlphabet(a_alphabet);
 139    }
 140   
 141    /**
 142    * Sets the value (allele) of this Gene to a random String according to the
 143    * valid alphabet and boundaries of length.
 144    *
 145    * @param a_numberGenerator the random number generator that should be used
 146    * to create any random values. It's important to use this generator to
 147    * maintain the user's flexibility to configure the genetic engine to use the
 148    * random number generator of their choice
 149    *
 150    * @author Klaus Meffert
 151    * @since 1.1
 152    */
 153  133 public void setToRandomValue(final RandomGenerator a_numberGenerator) {
 154  133 if (m_alphabet == null || m_alphabet.length() < 1) {
 155  2 throw new IllegalStateException("The valid alphabet is empty!");
 156    }
 157  131 if (m_maxLength < m_minLength || m_maxLength < 1) {
 158  1 throw new IllegalStateException(
 159    "Illegal valid maximum and/or minimum "
 160    + "length of alphabet!");
 161    }
 162    //randomize length of string
 163    //--------------------------
 164  130 int length;
 165  130 char value;
 166  130 int index;
 167  130 length = m_maxLength - m_minLength + 1;
 168  130 int i = a_numberGenerator.nextInt() % length;
 169  130 if (i < 0) {
 170  67 i = -i;
 171    }
 172  130 length = m_minLength + i;
 173    // For each character: randomize character value (which can be represented
 174    // by an integer value).
 175    //------------------------------------------------------------------------
 176  130 String newAllele = "";
 177  130 final int alphabetLength = m_alphabet.length();
 178  130 for (int j = 0; j < length; j++) {
 179  697 index = a_numberGenerator.nextInt(alphabetLength);
 180  697 value = m_alphabet.charAt(index);
 181  697 newAllele += value;
 182    }
 183    // Call setAllele to ensure extended verification.
 184    // -----------------------------------------------
 185  130 setAllele(newAllele);
 186    }
 187   
 188    /**
 189    * Sets the value and internal state of this Gene from the string
 190    * representation returned by a previous invocation of the
 191    * getPersistentRepresentation() method. This is an optional method but,
 192    * if not implemented, XML persistence and possibly other features will not
 193    * be available. An UnsupportedOperationException should be thrown if no
 194    * implementation is provided.
 195    *
 196    * @param a_representation the string representation retrieved from a prior
 197    * call to the getPersistentRepresentation() method
 198    *
 199    * @throws UnsupportedRepresentationException if this Gene implementation
 200    * does not support the given string representation
 201    *
 202    * @author Klaus Meffert
 203    * @since 1.1
 204    */
 205  22 public void setValueFromPersistentRepresentation(final String
 206    a_representation)
 207    throws UnsupportedRepresentationException {
 208  22 if (a_representation != null) {
 209  21 StringTokenizer tokenizer =
 210    new StringTokenizer(a_representation,
 211    PERSISTENT_FIELD_DELIMITER);
 212    // Make sure the representation contains the correct number of
 213    // fields. If not, throw an exception.
 214    // -----------------------------------------------------------
 215  21 if (tokenizer.countTokens() != 4) {
 216  1 throw new UnsupportedRepresentationException(
 217    "The format of the given persistent representation '" +
 218    a_representation + "'" +
 219    "is not recognized: it does not contain four tokens.");
 220    }
 221  20 String valueRepresentation;
 222  20 String alphabetRepresentation;
 223  20 String minLengthRepresentation;
 224  20 String maxLengthRepresentation;
 225  20 try {
 226  20 valueRepresentation =
 227    URLDecoder.decode(tokenizer.nextToken(), "UTF-8");
 228  20 minLengthRepresentation = tokenizer.nextToken();
 229  20 maxLengthRepresentation = tokenizer.nextToken();
 230  20 alphabetRepresentation =
 231    URLDecoder.decode(tokenizer.nextToken(), "UTF-8");
 232    }
 233    catch (UnsupportedEncodingException ex) {
 234  0 throw new Error("UTF-8 encoding should be always supported");
 235    }
 236    // Now parse and set the minimum length.
 237    // -------------------------------------
 238  20 try {
 239  20 m_minLength = Integer.parseInt(minLengthRepresentation);
 240    }
 241    catch (NumberFormatException e) {
 242  1 throw new UnsupportedRepresentationException(
 243    "The format of the given persistent representation " +
 244    "is not recognized: field 2 does not appear to be " +
 245    "an integer value.");
 246    }
 247    // Now parse and set the maximum length.
 248    // -------------------------------------
 249  19 try {
 250  19 m_maxLength = Integer.parseInt(maxLengthRepresentation);
 251    }
 252    catch (NumberFormatException e) {
 253  1 throw new UnsupportedRepresentationException(
 254    "The format of the given persistent representation " +
 255    "is not recognized: field 3 does not appear to be " +
 256    "an integer value.");
 257    }
 258  18 String tempValue;
 259    // Parse and set the representation of the value.
 260    // ----------------------------------------------
 261  18 if (valueRepresentation.equals("null")) {
 262  7 tempValue = null;
 263    }
 264    else {
 265  11 if (valueRepresentation.equals( ("\"\""))) {
 266  2 tempValue = "";
 267    }
 268    else {
 269  9 tempValue = valueRepresentation;
 270    }
 271    }
 272    //check if minLength and maxLength are violated.
 273    //----------------------------------------------
 274  18 if (tempValue != null) {
 275  11 if (m_minLength > tempValue.length()) {
 276  1 throw new UnsupportedRepresentationException(
 277    "The value given"
 278    + " is shorter than the allowed maximum length.");
 279    }
 280  10 if (m_maxLength < tempValue.length()) {
 281  1 throw new UnsupportedRepresentationException(
 282    "The value given"
 283    + " is longer than the allowed maximum length.");
 284    }
 285    }
 286    //check if all characters are within the alphabet.
 287    //------------------------------------------------
 288  16 if (!isValidAlphabet(tempValue, alphabetRepresentation)) {
 289  1 throw new UnsupportedRepresentationException("The value given"
 290    + " contains invalid characters.");
 291    }
 292  15 m_value = tempValue;
 293    // Now set the alphabet that should be valid.
 294    // ------------------------------------------
 295  15 m_alphabet = alphabetRepresentation;
 296    }
 297    }
 298   
 299    /**
 300    * Retrieves a string representation of this Gene that includes any
 301    * information required to reconstruct it at a later time, such as its
 302    * value and internal state. This string will be used to represent this
 303    * Gene in XML persistence. This is an optional method but, if not
 304    * implemented, XML persistence and possibly other features will not be
 305    * available. An UnsupportedOperationException should be thrown if no
 306    * implementation is provided.
 307    *
 308    * @return string representation of this Gene's current state
 309    * @throws UnsupportedOperationException to indicate that no implementation
 310    * is provided for this method
 311    *
 312    * @author Klaus Meffert
 313    * @since 1.1
 314    */
 315  31 public String getPersistentRepresentation()
 316    throws UnsupportedOperationException {
 317  31 try {
 318    // The persistent representation includes the value, minimum length,
 319    // maximum length and valid alphabet. Each is separated by a colon.
 320    // -----------------------------------------------------------------
 321  31 String s;
 322  31 if (m_value == null) {
 323  16 s = "null";
 324    }
 325    else {
 326  15 if (m_value.equals("")) {
 327  4 s = "\"\"";
 328    }
 329    else {
 330  11 s = m_value;
 331    }
 332    }
 333  31 return URLEncoder.encode("" + s, "UTF-8") +
 334    PERSISTENT_FIELD_DELIMITER + m_minLength +
 335    PERSISTENT_FIELD_DELIMITER + m_maxLength +
 336    PERSISTENT_FIELD_DELIMITER +
 337    URLEncoder.encode("" + m_alphabet, "UTF-8");
 338    }
 339    catch (UnsupportedEncodingException ex) {
 340  0 throw new Error("UTF-8 encoding should be supported");
 341    }
 342    }
 343   
 344    /**
 345    * Sets the value (allele) of this Gene to the new given value. This class
 346    * expects the value to be a String instance. If the value is shorter or
 347    * longer than the minimum or maximum length or any character is not within
 348    * the valid alphabet an exception is thrown.
 349    *
 350    * @param a_newValue the new value of this Gene instance
 351    *
 352    * @author Klaus Meffert
 353    * @since 1.1
 354    */
 355  2614 public void setAllele(final Object a_newValue) {
 356  2614 if (a_newValue != null) {
 357  383 String temp = (String) a_newValue;
 358  382 if (temp.length() < m_minLength ||
 359    temp.length() > m_maxLength) {
 360  4 throw new IllegalArgumentException(
 361    "The given value is too short or too long!");
 362    }
 363    //check for validity of alphabet.
 364    //-------------------------------
 365  378 if (!isValidAlphabet(temp, m_alphabet)) {
 366  3 throw new IllegalArgumentException("The given value contains"
 367    + " at least one invalid character.");
 368    }
 369  375 if (getConstraintChecker() != null) {
 370  3 if (!getConstraintChecker().verify(this, a_newValue, null, -1)) {
 371  1 return;
 372    }
 373    }
 374  374 m_value = temp;
 375    }
 376    else {
 377  2231 m_value = null;
 378    }
 379    }
 380   
 381    /**
 382    * Provides an implementation-independent means for creating new Gene
 383    * instances.
 384    *
 385    * @return a new Gene instance of the same type and with the same setup as
 386    * this concrete Gene
 387    *
 388    * @author Klaus Meffert
 389    * @since 1.1
 390    */
 391  2393 protected Gene newGeneInternal() {
 392  2393 try {
 393  2393 StringGene result = new StringGene(getConfiguration(), m_minLength,
 394    m_maxLength, m_alphabet);
 395  2393 result.setConstraintChecker(getConstraintChecker());
 396  2393 return result;
 397    }
 398    catch (InvalidConfigurationException iex) {
 399  0 throw new IllegalStateException(iex.getMessage());
 400    }
 401    }
 402   
 403    /**
 404    * Compares this StringGene with the specified object (which must also
 405    * be a StringGene) for order, which is determined by the String
 406    * value of this Gene compared to the one provided for comparison.
 407    *
 408    * @param a_other the StringGene to be compared to this StringGene
 409    * @return a negative int, zero, or a positive int as this object
 410    * is less than, equal to, or greater than the object provided for comparison
 411    *
 412    * @throws ClassCastException if the specified object's type prevents it
 413    * from being compared to this StringGene
 414    *
 415    * @author Klaus Meffert
 416    * @since 1.1
 417    */
 418  10287 public int compareTo(Object a_other) {
 419  10287 StringGene otherStringGene = (StringGene) a_other;
 420    // First, if the other gene (or its value) is null, then this is
 421    // the greater allele. Otherwise, just use the String's compareTo
 422    // method to perform the comparison.
 423    // ---------------------------------------------------------------
 424  3802 if (otherStringGene == null) {
 425  1 return 1;
 426    }
 427  3801 else if (otherStringGene.m_value == null) {
 428    // If our value is also null, then we're the same. Otherwise,
 429    // this is the greater gene.
 430    // ----------------------------------------------------------
 431  3499 if (m_value == null) {
 432  3497 if (isCompareApplicationData()) {
 433  6 return compareApplicationData(getApplicationData(),
 434    otherStringGene.getApplicationData());
 435    }
 436    else {
 437  3491 return 0;
 438    }
 439    }
 440    else {
 441  2 return 1;
 442    }
 443    }
 444    else {
 445  302 int res = m_value.compareTo(otherStringGene.m_value);
 446  302 if (res == 0) {
 447  26 if (isCompareApplicationData()) {
 448  10 return compareApplicationData(getApplicationData(),
 449    otherStringGene.getApplicationData());
 450    }
 451    else {
 452  16 return 0;
 453    }
 454    }
 455    else {
 456  276 return res;
 457    }
 458    }
 459    }
 460   
 461  179 public int size() {
 462  179 return m_value.length();
 463    }
 464   
 465  4 public int getMaxLength() {
 466  4 return m_maxLength;
 467    }
 468   
 469  4 public int getMinLength() {
 470  4 return m_minLength;
 471    }
 472   
 473  1 public void setMinLength(int m_minLength) {
 474  1 this.m_minLength = m_minLength;
 475    }
 476   
 477  2 public void setMaxLength(int m_maxLength) {
 478  2 this.m_maxLength = m_maxLength;
 479    }
 480   
 481  4 public String getAlphabet() {
 482  4 return m_alphabet;
 483    }
 484   
 485    /**
 486    * Sets the valid alphabet of the StringGene. The caller needs to care that
 487    * there are no doublettes in the alphabet. Otherwise there is no guarantee
 488    * for correct functioning of the class!
 489    * @param a_alphabet valid alphabet for allele
 490    *
 491    * @author Klaus Meffert
 492    * @since 1.1
 493    */
 494  7904 public void setAlphabet(String a_alphabet) {
 495  7904 m_alphabet = a_alphabet;
 496    }
 497   
 498    /**
 499    * Retrieves a string representation of this StringGene's value that
 500    * may be useful for display purposes.
 501    *
 502    * @return a string representation of this StringGene's value
 503    *
 504    * @author Klaus Meffert
 505    * @since 1.1
 506    */
 507  7 public String toString() {
 508  7 String s = "StringGene=";
 509  7 if (m_value == null) {
 510  5 s += "null";
 511    }
 512    else {
 513  2 if (m_value.equals("")) {
 514  1 s += "\"\"";
 515    }
 516    else {
 517  1 s += m_value;
 518    }
 519    }
 520  7 return s;
 521    }
 522   
 523    /**
 524    * Retrieves the String value of this Gene, which may be more convenient in
 525    * some cases than the more general getAllele() method.
 526    *
 527    * @return the String value of this Gene
 528    *
 529    * @since 1.1
 530    */
 531  163 public String stringValue() {
 532  163 return m_value;
 533    }
 534   
 535    /**
 536    * Checks whether a string value is valid concerning a given alphabet.
 537    * @param a_value the value to check
 538    * @param a_alphabet the valid alphabet to check against
 539    * @return true: given string value is valid
 540    *
 541    * @author Klaus Meffert
 542    * @since 1.1
 543    */
 544  394 private boolean isValidAlphabet(String a_value, String a_alphabet) {
 545  394 if (a_value == null || a_value.length() < 1) {
 546  13 return true;
 547    }
 548  381 if (a_alphabet == null) {
 549  23 return true;
 550    }
 551  358 if (a_alphabet.length() < 1) {
 552  2 return false;
 553    }
 554    // Loop over all characters of a_value.
 555    // ------------------------------------
 556  356 int length = a_value.length();
 557  356 char c;
 558  356 for (int i = 0; i < length; i++) {
 559  2312 c = a_value.charAt(i);
 560  2312 if (a_alphabet.indexOf(c) < 0) {
 561  2 return false;
 562    }
 563    }
 564  354 return true;
 565    }
 566   
 567    /**
 568    * Applies a mutation of a given intensity (percentage) onto the atomic
 569    * element at given index (NumberGenes only have one atomic element).
 570    * @param index index of atomic element, between 0 and size()-1
 571    * @param a_percentage percentage of mutation (greater than -1 and smaller
 572    * than 1).
 573    *
 574    * @author Klaus Meffert
 575    * @since 1.1
 576    */
 577  161 public void applyMutation(int index, double a_percentage) {
 578  161 String s = stringValue();
 579  161 int index2 = -1;
 580  161 boolean randomize;
 581  161 int len = 0;
 582  161 if (m_alphabet != null) {
 583  155 len = m_alphabet.length();
 584  155 if (len < 1) {
 585    // Does mutation make sense here?
 586    // ------------------------------
 587  1 randomize = true;
 588    }
 589    else {
 590  154 randomize = false;
 591    }
 592    }
 593    else {
 594  6 randomize = true;
 595    }
 596  161 char newValue;
 597  161 RandomGenerator rn = getConfiguration().getRandomGenerator();
 598  161 if (!randomize) {
 599  154 int indexC = m_alphabet.indexOf(s.charAt(index));
 600  154 index2 = indexC + (int) Math.round(len * a_percentage);
 601    // If index of new character out of bounds then randomly choose a new
 602    // character. This randomness is assumed to help in the process of
 603    // evolution.
 604    // ------------------------------------------------------------------
 605  154 if (index2 < 0 || index2 >= len) {
 606  70 index2 = rn.nextInt(len);
 607    }
 608  154 newValue = m_alphabet.charAt(index2);
 609    }
 610    else {
 611  7 index2 = rn.nextInt(256);
 612  6 newValue = (char) index2;
 613    }
 614    // Set mutated character by concatenating the String with it.
 615    // ----------------------------------------------------------
 616  160 if (s == null) {
 617  1 s = "" + newValue;
 618    }
 619    else {
 620  159 s = s.substring(0, index) + newValue + s.substring(index + 1);
 621    }
 622  159 setAllele(s);
 623    }
 624   
 625  12013 protected Object getInternalValue() {
 626  12013 return m_value;
 627    }
 628    }