/**
  * Clones an object by using clone handlers. If no deep cloning possible, then return the
  * reference.
  *
  * @param a_object the object to clone
  * @return the cloned object, or the object itself if no coning supported
  * @throws Exception
  * @author Klaus Meffert
  * @since 2.6
  */
 protected Object cloneObject(Object a_object) throws Exception {
   if (a_object == null) {
     return null;
   }
   // Try to clone via a registered clone handler.
   // --------------------------------------------
   ICloneHandler cloner =
       getConfiguration().getJGAPFactory().getCloneHandlerFor(a_object, a_object.getClass());
   if (cloner != null) {
     return cloner.perform(a_object, null, this);
   } else {
     // No cloning supported, so just return the reference.
     // ---------------------------------------------------
     return a_object;
   }
 }
 /**
  * Returns a copy of this Chromosome. The returned instance can evolve independently of this
  * instance. Note that, if possible, this method will first attempt to acquire a Chromosome
  * instance from the active ChromosomePool (if any) and set its value appropriately before
  * returning it. If that is not possible, then a new Chromosome instance will be constructed and
  * its value set appropriately before returning.
  *
  * @return copy of this Chromosome
  * @throws IllegalStateException instead of CloneNotSupportedException
  * @author Neil Rotstan
  * @author Klaus Meffert
  * @since 1.0
  */
 public synchronized Object clone() {
   // Before doing anything, make sure that a Configuration object
   // has been set on this Chromosome. If not, then throw an
   // IllegalStateException.
   // ------------------------------------------------------------
   if (getConfiguration() == null) {
     throw new IllegalStateException(
         "The active Configuration object must be set on this "
             + "Chromosome prior to invocation of the clone() method.");
   }
   IChromosome copy = null;
   // Now, first see if we can pull a Chromosome from the pool and just
   // set its gene values (alleles) appropriately.
   // ------------------------------------------------------------
   IChromosomePool pool = getConfiguration().getChromosomePool();
   if (pool != null) {
     copy = pool.acquireChromosome();
     if (copy != null) {
       Gene[] genes = copy.getGenes();
       for (int i = 0; i < size(); i++) {
         genes[i].setAllele(getGene(i).getAllele());
       }
     }
   }
   try {
     if (copy == null) {
       // We couldn't fetch a Chromosome from the pool, so we need to create
       // a new one. First we make a copy of each of the Genes. We explicity
       // use the Gene at each respective gene location (locus) to create the
       // new Gene that is to occupy that same locus in the new Chromosome.
       // -------------------------------------------------------------------
       int size = size();
       if (size > 0) {
         Gene[] copyOfGenes = new Gene[size];
         for (int i = 0; i < copyOfGenes.length; i++) {
           copyOfGenes[i] = getGene(i).newGene();
           Object allele = getGene(i).getAllele();
           if (allele != null) {
             IJGAPFactory factory = getConfiguration().getJGAPFactory();
             if (factory != null) {
               ICloneHandler cloner = factory.getCloneHandlerFor(allele, allele.getClass());
               if (cloner != null) {
                 try {
                   allele = cloner.perform(allele, null, this);
                 } catch (Exception ex) {
                   throw new RuntimeException(ex);
                 }
               }
             }
           }
           copyOfGenes[i].setAllele(allele);
         }
         // Now construct a new Chromosome with the copies of the genes and
         // return it. Also clone the IApplicationData object later on.
         // ---------------------------------------------------------------
         if (getClass() == Chromosome.class) {
           copy = new Chromosome(getConfiguration(), copyOfGenes);
         } else {
           copy = (IChromosome) getConfiguration().getSampleChromosome().clone();
           copy.setGenes(copyOfGenes);
         }
       } else {
         if (getClass() == Chromosome.class) {
           copy = new Chromosome(getConfiguration());
         } else {
           copy = (IChromosome) getConfiguration().getSampleChromosome().clone();
         }
       }
     }
     copy.setFitnessValue(m_fitnessValue);
     // Clone constraint checker.
     // -------------------------
     copy.setConstraintChecker(getConstraintChecker());
   } catch (InvalidConfigurationException iex) {
     throw new IllegalStateException(iex.getMessage());
   }
   // Also clone the IApplicationData object.
   // ---------------------------------------
   try {
     copy.setApplicationData(cloneObject(getApplicationData()));
   } catch (Exception ex) {
     throw new IllegalStateException(ex.getMessage());
   }
   // Clone multi-objective object if necessary and possible.
   // -------------------------------------------------------
   if (m_multiObjective != null) {
     if (getClass() == Chromosome.class) {
       try {
         ((Chromosome) copy).setMultiObjectives((List) cloneObject(m_multiObjective));
       } catch (Exception ex) {
         throw new IllegalStateException(ex.getMessage());
       }
     }
   }
   return copy;
 }