KickJava   Java API By Example, From Geeks To Geeks.

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


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, IPersistentRepresentation {
43   /** String containing the CVS revision. Read out via reflection!*/
44   private final static String JavaDoc CVS_REVISION = "$Revision: 1.55 $";
45
46   /**
47    * This field separates gene class name from the gene persistent representation
48    * string. '*' does not work properly with URLEncoder!
49    */

50   public final static String JavaDoc GENE_DELIMITER = "#";
51
52   /**
53    * Represents the heading delimiter that is used to separate genes in the
54    * persistent representation of CompositeGene instances.
55    */

56   public final static String JavaDoc GENE_DELIMITER_HEADING = "<";
57
58   /**
59    * Represents the closing delimiter that is used to separate genes in the
60    * persistent representation of CompositeGene instances.
61    */

62   public final static String JavaDoc GENE_DELIMITER_CLOSING = ">";
63
64   private Gene m_geneTypeAllowed;
65
66   /**
67    * The genes contained in this CompositeGene
68    *
69    * @author Klaus Meffert
70    * @since 1.1
71    */

72   private List m_genes;
73
74   /**
75    * Default constructor.<p>
76    * Attention: The configuration used is the one set with the static method
77    * Genotype.setConfiguration.
78    * @throws InvalidConfigurationException
79    *
80    * @author Klaus Meffert
81    * @since 1.1
82    */

83   public CompositeGene()
84       throws InvalidConfigurationException {
85     this(Genotype.getStaticConfiguration());
86   }
87
88   /**
89    * @param a_config the configuration to use
90    * @throws InvalidConfigurationException
91    *
92    * @author Klaus Meffert
93    * @since 3.0
94    */

95   public CompositeGene(Configuration a_config)
96       throws InvalidConfigurationException {
97     this(a_config, null);
98   }
99
100   /**
101    * Allows to specify which Gene implementation is allowed to be added to the
102    * CompositeGene.
103    *
104    * @param a_config the configuration to use
105    * @param a_geneTypeAllowed the class of Genes to be allowed to be added to
106    * the CompositeGene
107    * @throws InvalidConfigurationException
108    *
109    * @author Klaus Meffert
110    * @since 2.0
111    */

112   public CompositeGene(final Configuration a_config,
113                        final Gene a_geneTypeAllowed)
114       throws InvalidConfigurationException {
115     super(a_config);
116     m_genes = new Vector();
117     if (a_geneTypeAllowed != null) {
118       m_geneTypeAllowed = a_geneTypeAllowed;
119     }
120   }
121
122   /**
123    * Adds a gene to the CompositeGene
124    * @param a_gene the gene to add
125    */

126   public void addGene(final Gene a_gene) {
127     addGene(a_gene, false);
128   }
129
130   /**
131    * @return the gene type allowed, or null if any type allowed
132    *
133    * @author Klaus Meffert
134    * @since 2.4
135    */

136   public Gene getGeneTypeAllowed() {
137     return m_geneTypeAllowed;
138   }
139
140   /**
141    * Adds a gene to the CompositeGene's container. See comments in class
142    * header for additional details about equality (concerning "strict" param.)
143    *
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   public void addGene(final Gene a_gene, final boolean a_strict) {
154     if (a_gene == null) {
155       throw new IllegalArgumentException JavaDoc("Gene instance must not be null!");
156     }
157     if (m_geneTypeAllowed != null) {
158       if (!a_gene.getClass().getName().equals(m_geneTypeAllowed.getClass().
159                                               getName())) {
160         throw new IllegalArgumentException JavaDoc("Adding a "
161                                            + a_gene.getClass().getName()
162                                            + " has been forbidden!");
163       }
164     }
165     // Check if gene already exists.
166
// -----------------------------
167
boolean containsGene;
168     if (!a_strict) {
169       containsGene = containsGeneByIdentity(a_gene);
170     }
171     else {
172       containsGene = m_genes.contains(a_gene);
173     }
174     if (containsGene) {
175       throw new IllegalArgumentException JavaDoc("The gene is already contained"
176                                          + " in the CompositeGene!");
177     }
178     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   public boolean removeGeneByIdentity(final Gene a_gene) {
192     int size = size();
193     if (size < 1) {
194       return false;
195     }
196     else {
197       for (int i = 0; i < size; i++) {
198         if (geneAt(i) == a_gene) {
199           m_genes.remove(i);
200           return true;
201         }
202       }
203     }
204     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   public boolean removeGene(final Gene a_gene) {
218     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   public void cleanup() {
229     Gene gene;
230     int size = m_genes.size();
231     for (int i = 0; i < size; i++) {
232       gene = (Gene) m_genes.get(i);
233       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   public void setToRandomValue(final RandomGenerator a_numberGenerator) {
248     if (a_numberGenerator == null) {
249       throw new IllegalArgumentException JavaDoc("Random generatoe must not be null!");
250     }
251     Gene gene;
252     int size = m_genes.size();
253     for (int i = 0; i < size; i++) {
254       gene = (Gene) m_genes.get(i);
255       gene.setToRandomValue(a_numberGenerator);
256     }
257   }
258
259   /**
260    * See interface Gene for description.
261    *
262    * @param a_representation the string representation retrieved from a prior
263    * call to the getPersistentRepresentation() method
264    *
265    * @throws UnsupportedRepresentationException
266    *
267    * @author Klaus Meffert
268    * @author Audrius Meskauskas
269    * @since 1.1
270    */

271   public void setValueFromPersistentRepresentation(String JavaDoc a_representation)
272       throws UnsupportedRepresentationException {
273     if (a_representation != null) {
274       try {
275         // Remove the old content.
276
// -----------------------
277
m_genes.clear();
278         List r = split(a_representation);
279         Iterator iter = r.iterator();
280         StringTokenizer st;
281         String JavaDoc clas;
282         String JavaDoc representation;
283         String JavaDoc g;
284         Gene gene;
285         while (iter.hasNext()) {
286           g = URLDecoder.decode( (String JavaDoc) iter.next(), "UTF-8");
287           st = new StringTokenizer(g, GENE_DELIMITER);
288           if (st.countTokens() != 2)
289             throw new UnsupportedRepresentationException("In " + g + ", " +
290                 "expecting two tokens, separated by " + GENE_DELIMITER);
291           clas = st.nextToken();
292           representation = st.nextToken();
293           gene = createGene(clas, representation);
294           addGene(gene);
295         }
296       }
297       catch (Exception JavaDoc ex) {
298         throw new UnsupportedRepresentationException(ex.toString());
299       }
300     }
301   }
302
303   /**
304    * Creates a new instance of gene.
305    *
306    * @param a_geneClassName name of the gene class
307    * @param a_persistentRepresentation persistent representation of the gene to
308    * create (could be obtained via getPersistentRepresentation)
309    *
310    * @return newly created gene
311    * @throws Exception
312    *
313    * @author Klaus Meffert
314    */

315   protected Gene createGene(String JavaDoc a_geneClassName,
316                             String JavaDoc a_persistentRepresentation)
317       throws Exception JavaDoc {
318     Class JavaDoc geneClass = Class.forName(a_geneClassName);
319     Constructor constr = geneClass.getConstructor(new Class JavaDoc[] {Configuration.class});
320     Gene gene = (Gene) constr.newInstance(new Object JavaDoc[] {getConfiguration()});
321     gene.setValueFromPersistentRepresentation(a_persistentRepresentation);
322     return gene;
323   }
324
325   /**
326    * See interface Gene for description.
327    *
328    * @return string representation of this Gene's current state
329    * @throws UnsupportedOperationException
330    *
331    * @author Klaus Meffert
332    * @author Audrius Meskauskas
333    * @since 1.1
334    */

335   public String JavaDoc getPersistentRepresentation()
336       throws UnsupportedOperationException JavaDoc {
337     StringBuffer JavaDoc b = new StringBuffer JavaDoc();
338     Iterator iter = m_genes.iterator();
339     Gene gene;
340     while (iter.hasNext()) {
341       gene = (Gene) iter.next();
342       b.append(GENE_DELIMITER_HEADING);
343       try {
344         b.append(
345             URLEncoder.encode(
346             gene.getClass().getName() +
347             GENE_DELIMITER +
348             gene.getPersistentRepresentation(), "UTF-8"
349             ));
350       }
351       catch (UnsupportedEncodingException uex) {
352         throw new RuntimeException JavaDoc("UTF-8 should always be supported!", uex);
353       }
354       b.append(GENE_DELIMITER_CLOSING);
355     }
356     return b.toString();
357   }
358
359   /**
360    * Retrieves the value represented by this Gene. All values returned
361    * by this class will be Vector instances. Each element of the Vector
362    * represents the allele of the corresponding gene in the CompositeGene's
363    * container
364    *
365    * @return the value of this Gene
366    *
367    * @author Klaus Meffert
368    * @since 1.1
369    */

370   public Object JavaDoc getAllele() {
371     List alleles = new Vector();
372     Gene gene;
373     int size = m_genes.size();
374     for (int i = 0; i < size; i++) {
375       gene = (Gene) m_genes.get(i);
376       alleles.add(gene.getAllele());
377     }
378     return alleles;
379   }
380
381   /**
382    * Sets the value of the contained Genes to the new given value. This class
383    * expects the value to be of a Vector type. Each element of the Vector
384    * must conform with the type of the gene in the CompositeGene's container
385    * at the corresponding position.
386    *
387    * @param a_newValue the new value of this Gene instance
388    *
389    * @author Klaus Meffert
390    * @since 1.1
391    */

392   public void setAllele(Object JavaDoc a_newValue) {
393     if (! (a_newValue instanceof List)) {
394       throw new IllegalArgumentException JavaDoc(
395           "The expected type of the allele"
396           + " is a List descendent.");
397     }
398     if (getConstraintChecker() != null) {
399       if (!getConstraintChecker().verify(this, a_newValue, null, -1)) {
400         return;
401       }
402     }
403     List alleles = (List) a_newValue;
404     Gene gene;
405     for (int i = 0; i < alleles.size(); i++) {
406       gene = (Gene) m_genes.get(i);
407       gene.setAllele(alleles.get(i));
408     }
409   }
410
411   /**
412    * Provides an implementation-independent means for creating new Gene
413    * instances.
414    *
415    * @return a new Gene instance of the same type and with the same setup as
416    * this concrete Gene
417    *
418    * @author Klaus Meffert
419    * @since 1.1
420    */

421   protected Gene newGeneInternal() {
422     try {
423       CompositeGene compositeGene = new CompositeGene(getConfiguration());
424       compositeGene.setConstraintChecker(getConstraintChecker());
425       Gene gene;
426       int geneSize = m_genes.size();
427       for (int i = 0; i < geneSize; i++) {
428         gene = (Gene) m_genes.get(i);
429         compositeGene.addGene(gene.newGene(), false);
430       }
431       return compositeGene;
432     }
433     catch (InvalidConfigurationException iex) {
434       throw new IllegalStateException JavaDoc(iex.getMessage());
435     }
436   }
437
438   /**
439    * Compares this CompositeGene with the specified object for order. A
440    * false value is considered to be less than a true value. A null value
441    * is considered to be less than any non-null value.
442    *
443    * @param a_other the CompositeGene to be compared
444    * @return a negative integer, zero, or a positive integer as this object
445    * is less than, equal to, or greater than the specified object
446    *
447    * @throws ClassCastException if the specified object's type prevents it
448    * from being compared to this CompositeGene
449    *
450    * @author Klaus Meffert
451    * @since 1.1
452    */

453   public int compareTo(Object JavaDoc a_other) {
454     // First, if the other gene (or its value) is null, then this is
455
// the greater allele. Otherwise, just use the contained genes' compareTo
456
// method to perform the comparison.
457
// ---------------------------------------------------------------
458
if (a_other == null) {
459       return 1;
460     }
461     if (! (a_other instanceof CompositeGene)) {
462       return this.getClass().getName().compareTo(a_other.getClass().getName());
463     }
464     CompositeGene otherCompositeGene = (CompositeGene) a_other;
465     if (otherCompositeGene.isEmpty()) {
466       // If our value is also null, then we're the same. Otherwise,
467
// this is the greater gene.
468
// ----------------------------------------------------------
469
if (isEmpty()) {
470         return 0;
471       }
472       else {
473         return 1;
474       }
475     }
476     else {
477       // Compare each gene against each other.
478
// -------------------------------------
479
int numberGenes = Math.min(size(), otherCompositeGene.size());
480       Gene gene1;
481       Gene gene2;
482       for (int i = 0; i < numberGenes; i++) {
483         gene1 = geneAt(i);
484         gene2 = otherCompositeGene.geneAt(i);
485         int result = gene1.compareTo(gene2);
486         if (result != 0) {
487           return result;
488         }
489       }
490       // If everything is equal until now the CompositeGene with more
491
// contained genes wins.
492
// ------------------------------------------------------------
493
if (size() == otherCompositeGene.size()) {
494         if (isCompareApplicationData()) {
495           return compareApplicationData(getApplicationData(),
496                                         otherCompositeGene.getApplicationData());
497         }
498         else {
499           return 0;
500         }
501       }
502       else {
503         if (size() > otherCompositeGene.size()) {
504           return 1;
505         }
506         else {
507           return -1;
508         }
509       }
510     }
511   }
512
513   /**
514    * Retrieves a string representation of this CompositeGene's value that
515    * may be useful for display purposes.
516    * @return string representation of this CompositeGene's value. Every
517    * contained gene's string representation is delimited by the given
518    * delimiter
519    *
520    * @author Neil Rotstan
521    * @author Klaus Meffert
522    * @author Audrius Meskauskas
523    * @since 1.1
524    */

525   public String JavaDoc toString() {
526     if (m_genes.isEmpty()) {
527       return "CompositeGene=null";
528     }
529     else {
530       String JavaDoc result = "CompositeGene=(";
531       Gene gene;
532       for (int i = 0; i < m_genes.size(); i++) {
533         gene = (Gene) m_genes.get(i);
534         result += gene;
535         if (i < m_genes.size() - 1) {
536           result += GENE_DELIMITER;
537         }
538       }
539       return result + ")";
540     }
541   }
542
543   /**
544    * @return true: no genes contained, false otherwise
545    *
546    * @author Klaus Meffert
547    * @since 1.1
548    */

549   public boolean isEmpty() {
550     return m_genes.isEmpty() ? true : false;
551   }
552
553   /**
554    * @param a_index index to return the gene at
555    * @return the gene at the given index
556    *
557    * @author Klaus Meffert
558    * @since 1.1
559    */

560   public Gene geneAt(int a_index) {
561     return (Gene) m_genes.get(a_index);
562   }
563
564   /**
565    * @return the number of genes contained
566    *
567    * @author Klaus Meffert
568    * @since 1.1
569    */

570   public int size() {
571     return m_genes.size();
572   }
573
574   /**
575    * Checks whether a specific gene is already contained. The determination
576    * will be done by checking for identity and not using the equal method!
577    *
578    * @param gene the gene under test
579    * @return true: the given gene object is contained
580    *
581    * @author Klaus Meffert
582    * @since 1.1
583    */

584   public boolean containsGeneByIdentity(Gene gene) {
585     boolean result;
586     int size = size();
587     if (size < 1) {
588       result = false;
589     }
590     else {
591       result = false;
592       for (int i = 0; i < size; i++) {
593         //check for identity
594
//------------------
595
if (geneAt(i) == gene) {
596           result = true;
597           break;
598         }
599       }
600     }
601     return result;
602   }
603
604   /**
605    * Don't use this method, is makes no sense here. It is just there to
606    * satisfy the Gene interface. Instead, loop over all contained genes and
607    * call their applyMutation method.
608    * @param a_index does not matter here
609    * @param a_percentage does not matter here
610    *
611    * @author Klaus Meffert
612    * @since 1.1
613    */

614   public void applyMutation(int a_index, double a_percentage) {
615     // problem here: size() of CompositeGene not equal to (different)
616
// sizes of contained genes.
617
// Solution: Don't use CompositeGene.applyMutation, instead loop
618
// over all contained genes and call their method
619
// -------------------------------------------------------------
620
throw new RuntimeException JavaDoc("applyMutation may not be called for "
621                                + "a CompositeGene. Call this method for each"
622                                + " gene contained in the CompositeGene.");
623   }
624
625   /**
626    * Splits the input a_string into individual gene representations.
627    *
628    * @param a_string the string to split
629    * @return the elements of the returned array are the persistent
630    * representation strings of the gene's components
631    * @throws UnsupportedRepresentationException
632    *
633    * @author Audrius Meskauskas
634    * @since 2.0
635    */

636   protected static final List split(String JavaDoc a_string)
637       throws UnsupportedRepresentationException {
638     List a = Collections.synchronizedList(new ArrayList());
639     StringTokenizer st = new StringTokenizer
640         (a_string, GENE_DELIMITER_HEADING + GENE_DELIMITER_CLOSING, true);
641     while (st.hasMoreTokens()) {
642       if (!st.nextToken().equals(GENE_DELIMITER_HEADING)) {
643         throw new UnsupportedRepresentationException(a_string + " no open tag");
644       }
645       String JavaDoc n = st.nextToken();
646       if (n.equals(GENE_DELIMITER_CLOSING)) {
647         a.add(""); /* Empty token */
648       }
649       else {
650         a.add(n);
651         if (!st.nextToken().equals(GENE_DELIMITER_CLOSING)) {
652           throw new UnsupportedRepresentationException
653               (a_string + " no close tag");
654         }
655       }
656     }
657     return a;
658   }
659
660   /**
661    * Retrieves the hash code value for this Gene.
662    *
663    * @return this Gene's hash code
664    *
665    * @author Klaus Meffert
666    * @since 2.2
667    */

668   public int hashCode() {
669     int hashCode = 1;
670     int geneHashcode;
671     for (int i = 0; i < size(); i++) {
672       geneHashcode = geneAt(i).hashCode();
673       hashCode = 31 * hashCode + geneHashcode;
674     }
675     return hashCode;
676   }
677
678   /**
679    * This method is not called internally because BaseGene.getAllele() is
680    * overridden here!
681    * @return always null
682    */

683   protected Object JavaDoc getInternalValue() {
684     return null;
685   }
686 }
687
Popular Tags