KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jgap > impl > StringGene


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 implements IPersistentRepresentation {
31   //Constants for ready-to-use alphabets or serving as part of concetenation
32
public static final String JavaDoc ALPHABET_CHARACTERS_UPPER =
33       "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
34
35   public static final String JavaDoc ALPHABET_CHARACTERS_LOWER =
36       "abcdefghijklmnopqrstuvwxyz";
37
38   public static final String JavaDoc ALPHABET_CHARACTERS_DIGITS = "0123456789";
39
40   public static final String JavaDoc ALPHABET_CHARACTERS_SPECIAL = "+.*/\\,;@";
41
42   /** String containing the CVS revision. Read out via reflection!*/
43   private final static String JavaDoc CVS_REVISION = "$Revision: 1.55 $";
44
45   private int m_minLength;
46
47   private int m_maxLength;
48
49   private String JavaDoc m_alphabet;
50
51   /**
52    * References the internal String value (allele) of this Gene.
53    */

54   private String JavaDoc 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   public StringGene()
69       throws InvalidConfigurationException {
70     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   public StringGene(final Configuration a_config)
85       throws InvalidConfigurationException {
86     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   public StringGene(final Configuration a_config, final int a_minLength,
103                     final int a_maxLength)
104       throws InvalidConfigurationException {
105     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   public StringGene(final Configuration a_config, final int a_minLength,
123                     final int a_maxLength, final String JavaDoc a_alphabet)
124       throws InvalidConfigurationException {
125     super(a_config);
126     if (a_minLength < 0) {
127       throw new IllegalArgumentException JavaDoc(
128           "minimum length must be greater than"
129           + " zero!");
130     }
131     if (a_maxLength < a_minLength) {
132       throw new IllegalArgumentException JavaDoc(
133           "minimum length must be smaller than"
134           + " or equal to maximum length!");
135     }
136     m_minLength = a_minLength;
137     m_maxLength = a_maxLength;
138     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   public void setToRandomValue(final RandomGenerator a_numberGenerator) {
154     if (m_alphabet == null || m_alphabet.length() < 1) {
155       throw new IllegalStateException JavaDoc("The valid alphabet is empty!");
156     }
157     if (m_maxLength < m_minLength || m_maxLength < 1) {
158       throw new IllegalStateException JavaDoc(
159           "Illegal valid maximum and/or minimum "
160           + "length of alphabet!");
161     }
162     //randomize length of string
163
//--------------------------
164
int length;
165     char value;
166     int index;
167     length = m_maxLength - m_minLength + 1;
168     int i = a_numberGenerator.nextInt() % length;
169     if (i < 0) {
170       i = -i;
171     }
172     length = m_minLength + i;
173     // For each character: randomize character value (which can be represented
174
// by an integer value).
175
//------------------------------------------------------------------------
176
String JavaDoc newAllele = "";
177     final int alphabetLength = m_alphabet.length();
178     for (int j = 0; j < length; j++) {
179       index = a_numberGenerator.nextInt(alphabetLength);
180       value = m_alphabet.charAt(index);
181       newAllele += value;
182     }
183     // Call setAllele to ensure extended verification.
184
// -----------------------------------------------
185
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   public void setValueFromPersistentRepresentation(final String JavaDoc
206       a_representation)
207       throws UnsupportedRepresentationException {
208     if (a_representation != null) {
209       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
if (tokenizer.countTokens() != 4) {
216         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       String JavaDoc valueRepresentation;
222       String JavaDoc alphabetRepresentation;
223       String JavaDoc minLengthRepresentation;
224       String JavaDoc maxLengthRepresentation;
225       try {
226         valueRepresentation =
227             URLDecoder.decode(tokenizer.nextToken(), "UTF-8");
228         minLengthRepresentation = tokenizer.nextToken();
229         maxLengthRepresentation = tokenizer.nextToken();
230         alphabetRepresentation =
231             URLDecoder.decode(tokenizer.nextToken(), "UTF-8");
232       }
233       catch (UnsupportedEncodingException ex) {
234         throw new Error JavaDoc("UTF-8 encoding should be always supported");
235       }
236       // Now parse and set the minimum length.
237
// -------------------------------------
238
try {
239         m_minLength = Integer.parseInt(minLengthRepresentation);
240       }
241       catch (NumberFormatException JavaDoc e) {
242         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
try {
250         m_maxLength = Integer.parseInt(maxLengthRepresentation);
251       }
252       catch (NumberFormatException JavaDoc e) {
253         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       String JavaDoc tempValue;
259       // Parse and set the representation of the value.
260
// ----------------------------------------------
261
if (valueRepresentation.equals("null")) {
262         tempValue = null;
263       }
264       else {
265         if (valueRepresentation.equals( ("\"\""))) {
266           tempValue = "";
267         }
268         else {
269           tempValue = valueRepresentation;
270         }
271       }
272       //check if minLength and maxLength are violated.
273
//----------------------------------------------
274
if (tempValue != null) {
275         if (m_minLength > tempValue.length()) {
276           throw new UnsupportedRepresentationException(
277               "The value given"
278               + " is shorter than the allowed maximum length.");
279         }
280         if (m_maxLength < tempValue.length()) {
281           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
if (!isValidAlphabet(tempValue, alphabetRepresentation)) {
289         throw new UnsupportedRepresentationException("The value given"
290             + " contains invalid characters.");
291       }
292       m_value = tempValue;
293       // Now set the alphabet that should be valid.
294
// ------------------------------------------
295
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   public String JavaDoc getPersistentRepresentation()
316       throws UnsupportedOperationException JavaDoc {
317     try {
318       // The persistent representation includes the value, minimum length,
319
// maximum length and valid alphabet. Each is separated by a colon.
320
// -----------------------------------------------------------------
321
String JavaDoc s;
322       if (m_value == null) {
323         s = "null";
324       }
325       else {
326         if (m_value.equals("")) {
327           s = "\"\"";
328         }
329         else {
330           s = m_value;
331         }
332       }
333       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       throw new Error JavaDoc("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   public void setAllele(final Object JavaDoc a_newValue) {
356     if (a_newValue != null) {
357       String JavaDoc temp = (String JavaDoc) a_newValue;
358       if (temp.length() < m_minLength ||
359           temp.length() > m_maxLength) {
360         throw new IllegalArgumentException JavaDoc(
361             "The given value is too short or too long!");
362       }
363       //check for validity of alphabet.
364
//-------------------------------
365
if (!isValidAlphabet(temp, m_alphabet)) {
366         throw new IllegalArgumentException JavaDoc("The given value contains"
367                                            + " at least one invalid character.");
368       }
369       if (getConstraintChecker() != null) {
370         if (!getConstraintChecker().verify(this, a_newValue, null, -1)) {
371           return;
372         }
373       }
374       m_value = temp;
375     }
376     else {
377       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   protected Gene newGeneInternal() {
392     try {
393       StringGene result = new StringGene(getConfiguration(), m_minLength,
394                                          m_maxLength, m_alphabet);
395       result.setConstraintChecker(getConstraintChecker());
396       return result;
397     }
398     catch (InvalidConfigurationException iex) {
399       throw new IllegalStateException JavaDoc(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   public int compareTo(Object JavaDoc a_other) {
419     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
if (otherStringGene == null) {
425       return 1;
426     }
427     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
if (m_value == null) {
432         if (isCompareApplicationData()) {
433           return compareApplicationData(getApplicationData(),
434                                         otherStringGene.getApplicationData());
435         }
436         else {
437           return 0;
438         }
439       }
440       else {
441         return 1;
442       }
443     }
444     else {
445       int res = m_value.compareTo(otherStringGene.m_value);
446       if (res == 0) {
447         if (isCompareApplicationData()) {
448           return compareApplicationData(getApplicationData(),
449                                         otherStringGene.getApplicationData());
450         }
451         else {
452           return 0;
453         }
454       }
455       else {
456         return res;
457       }
458     }
459   }
460
461   public int size() {
462     return m_value.length();
463   }
464
465   public int getMaxLength() {
466     return m_maxLength;
467   }
468
469   public int getMinLength() {
470     return m_minLength;
471   }
472
473   public void setMinLength(int m_minLength) {
474     this.m_minLength = m_minLength;
475   }
476
477   public void setMaxLength(int m_maxLength) {
478     this.m_maxLength = m_maxLength;
479   }
480
481   public String JavaDoc getAlphabet() {
482     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   public void setAlphabet(String JavaDoc a_alphabet) {
495     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   public String JavaDoc toString() {
508     String JavaDoc s = "StringGene=";
509     if (m_value == null) {
510       s += "null";
511     }
512     else {
513       if (m_value.equals("")) {
514         s += "\"\"";
515       }
516       else {
517         s += m_value;
518       }
519     }
520     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   public String JavaDoc stringValue() {
532     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   private boolean isValidAlphabet(String JavaDoc a_value, String JavaDoc a_alphabet) {
545     if (a_value == null || a_value.length() < 1) {
546       return true;
547     }
548     if (a_alphabet == null) {
549       return true;
550     }
551     if (a_alphabet.length() < 1) {
552       return false;
553     }
554     // Loop over all characters of a_value.
555
// ------------------------------------
556
int length = a_value.length();
557     char c;
558     for (int i = 0; i < length; i++) {
559       c = a_value.charAt(i);
560       if (a_alphabet.indexOf(c) < 0) {
561         return false;
562       }
563     }
564     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   public void applyMutation(int index, double a_percentage) {
578     String JavaDoc s = stringValue();
579     int index2 = -1;
580     boolean randomize;
581     int len = 0;
582     if (m_alphabet != null) {
583       len = m_alphabet.length();
584       if (len < 1) {
585         // Does mutation make sense here?
586
// ------------------------------
587
randomize = true;
588       }
589       else {
590         randomize = false;
591       }
592     }
593     else {
594       randomize = true;
595     }
596     char newValue;
597     RandomGenerator rn = getConfiguration().getRandomGenerator();
598     if (!randomize) {
599       int indexC = m_alphabet.indexOf(s.charAt(index));
600       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
if (index2 < 0 || index2 >= len) {
606         index2 = rn.nextInt(len);
607       }
608       newValue = m_alphabet.charAt(index2);
609     }
610     else {
611       index2 = rn.nextInt(256);
612       newValue = (char) index2;
613     }
614     // Set mutated character by concatenating the String with it.
615
// ----------------------------------------------------------
616
if (s == null) {
617       s = "" + newValue;
618     }
619     else {
620       s = s.substring(0, index) + newValue + s.substring(index + 1);
621     }
622     setAllele(s);
623   }
624
625   protected Object JavaDoc getInternalValue() {
626     return m_value;
627   }
628 }
629
Popular Tags