CharacterChromosome.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-3.9.0).
003  * Copyright (c) 2007-2017 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at)
019  */
020 package org.jenetics;
021 
022 import static org.jenetics.CharacterGene.DEFAULT_CHARACTERS;
023 import static org.jenetics.internal.util.Equality.eq;
024 
025 import java.io.IOException;
026 import java.io.ObjectInputStream;
027 import java.io.ObjectOutputStream;
028 import java.io.Serializable;
029 import java.util.function.Supplier;
030 import java.util.stream.Collectors;
031 
032 import javax.xml.bind.annotation.XmlAccessType;
033 import javax.xml.bind.annotation.XmlAccessorType;
034 import javax.xml.bind.annotation.XmlAttribute;
035 import javax.xml.bind.annotation.XmlElement;
036 import javax.xml.bind.annotation.XmlRootElement;
037 import javax.xml.bind.annotation.XmlType;
038 import javax.xml.bind.annotation.adapters.XmlAdapter;
039 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
040 
041 import org.jenetics.internal.util.Equality;
042 import org.jenetics.internal.util.Hash;
043 import org.jenetics.internal.util.IntRef;
044 
045 import org.jenetics.util.CharSeq;
046 import org.jenetics.util.ISeq;
047 import org.jenetics.util.MSeq;
048 
049 /**
050  * CharacterChromosome which represents character sequences.
051  *
052  @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a>
053  @since 1.0
054  @version 3.0
055  */
056 @XmlJavaTypeAdapter(CharacterChromosome.Model.Adapter.class)
057 public class CharacterChromosome
058     extends
059         AbstractChromosome<CharacterGene>
060     implements
061         CharSequence,
062         Serializable
063 {
064     private static final long serialVersionUID = 2L;
065 
066     private transient CharSeq _validCharacters;
067 
068     /**
069      * Create a new chromosome from the given {@code genes} array. The genes
070      * array is copied, so changes to the given genes array doesn't effect the
071      * genes of this chromosome.
072      *
073      @param genes the genes that form the chromosome.
074      @throws NullPointerException if the given gene array is {@code null}.
075      @throws IllegalArgumentException if the length of the gene array is
076      *         smaller than one.
077      */
078     protected CharacterChromosome(final ISeq<CharacterGene> genes) {
079         super(genes);
080         _validCharacters = genes.get(0).getValidCharacters();
081     }
082 
083     /**
084      * Create a new chromosome with the {@code validCharacters} char set as
085      * valid characters.
086      *
087      @param validCharacters the valid characters for this chromosome.
088      @param length the length of the new chromosome.
089      @throws NullPointerException if the {@code validCharacters} is
090      *         {@code null}.
091      @throws IllegalArgumentException if the {@code length} is smaller than
092      *         one.
093      */
094     public CharacterChromosome(final CharSeq validCharacters, final int length) {
095         this(CharacterGene.seq(validCharacters, length));
096         _valid = true;
097     }
098 
099     @Override
100     public char charAt(final int index) {
101         return getGene(index).charValue();
102     }
103 
104     @Override
105     public CharacterChromosome subSequence(final int start, final int end) {
106         return new CharacterChromosome(_genes.subSeq(start, end));
107     }
108 
109     /**
110      @throws NullPointerException if the given gene array is {@code null}.
111      */
112     @Override
113     public CharacterChromosome newInstance(final ISeq<CharacterGene> genes) {
114         return new CharacterChromosome(genes);
115     }
116 
117     /**
118      * Create a new, <em>random</em> chromosome.
119      */
120     @Override
121     public CharacterChromosome newInstance() {
122         return new CharacterChromosome(_validCharacters, length());
123     }
124 
125     @Override
126     public int hashCode() {
127         return Hash.of(getClass())
128                 .and(super.hashCode())
129                 .and(_validCharacters).value();
130     }
131 
132     @Override
133     public boolean equals(final Object obj) {
134         return Equality.of(this, obj).test(cc ->
135             super.equals(obj&&
136             eq(_validCharacters, cc._validCharacters)
137         );
138     }
139 
140     @Override
141     public String toString() {
142         return toSeq().stream()
143             .map(Object::toString)
144             .collect(Collectors.joining());
145     }
146 
147     /**
148      * Returns an char array containing all of the elements in this chromosome
149      * in proper sequence.  If the chromosome fits in the specified array, it is
150      * returned therein. Otherwise, a new array is allocated with the length of
151      * this chromosome.
152      *
153      @since 3.0
154      *
155      @param array the array into which the elements of this chromosomes are to
156      *        be stored, if it is big enough; otherwise, a new array is
157      *        allocated for this purpose.
158      @return an array containing the elements of this chromosome
159      @throws NullPointerException if the given {@code array} is {@code null}
160      */
161     public char[] toArray(final char[] array) {
162         final char[] a = array.length >= length() ?
163             array : new char[length()];
164 
165         for (int i = length(); --i >= 0;) {
166             a[i= charAt(i);
167         }
168 
169         return a;
170     }
171 
172     /**
173      * Returns an char array containing all of the elements in this chromosome
174      * in proper sequence.
175      *
176      @since 3.0
177      *
178      @return an array containing the elements of this chromosome
179      */
180     public char[] toArray() {
181         return toArray(new char[length()]);
182     }
183 
184 
185     /**
186      * Create a new chromosome with the {@link CharacterGene#DEFAULT_CHARACTERS}
187      * char set as valid characters.
188      *
189      @param length the {@code length} of the new chromosome.
190      @return a new {@code CharacterChromosome} with the given parameter
191      @throws IllegalArgumentException if the {@code length} is smaller than
192      *         one.
193      */
194     public static CharacterChromosome of(final int length) {
195         return new CharacterChromosome(
196             CharacterGene.seq(DEFAULT_CHARACTERS, length)
197         );
198     }
199 
200     /**
201      * Create a new chromosome from the given genes (given as string).
202      *
203      @param alleles the character genes.
204      @param validChars the valid characters.
205      @return a new {@code CharacterChromosome} with the given parameter
206      @throws IllegalArgumentException if the genes string is empty.
207      */
208     public static CharacterChromosome of(
209         final String alleles,
210         final CharSeq validChars
211     ) {
212         final IntRef index = new IntRef();
213         final Supplier<CharacterGene> geneFactory = () -> CharacterGene.of(
214             alleles.charAt(index.value++), validChars
215         );
216 
217         final ISeq<CharacterGene> genes =
218             MSeq.<CharacterGene>ofLength(alleles.length())
219                 .fill(geneFactory)
220                 .toISeq();
221 
222         return new CharacterChromosome(genes);
223     }
224 
225     /**
226      * Create a new chromosome from the given genes (given as string).
227      *
228      @param alleles the character genes.
229      @return a new {@code CharacterChromosome} with the given parameter
230      @throws IllegalArgumentException if the genes string is empty.
231      */
232     public static CharacterChromosome of(final String alleles) {
233         return of(alleles, DEFAULT_CHARACTERS);
234     }
235 
236 
237     /* *************************************************************************
238      *  Java object serialization
239      * ************************************************************************/
240 
241     private void writeObject(final ObjectOutputStream out)
242         throws IOException
243     {
244         out.defaultWriteObject();
245 
246         out.writeInt(length());
247         out.writeObject(_validCharacters);
248 
249         for (CharacterGene gene : _genes) {
250             out.writeChar(gene.getAllele());
251         }
252     }
253 
254     private void readObject(final ObjectInputStream in)
255         throws IOException, ClassNotFoundException
256     {
257         in.defaultReadObject();
258 
259         final int length = in.readInt();
260         _validCharacters = (CharSeq)in.readObject();
261 
262         final MSeq<CharacterGene> genes = MSeq.ofLength(length);
263         for (int i = 0; i < length; ++i) {
264             final CharacterGene gene = CharacterGene.of(
265                 in.readChar(),
266                 _validCharacters
267             );
268             genes.set(i, gene);
269         }
270 
271         _genes = genes.toISeq();
272     }
273 
274     /* *************************************************************************
275      *  JAXB object serialization
276      * ************************************************************************/
277 
278     @XmlRootElement(name = "character-chromosome")
279     @XmlType(name = "org.jenetics.CharacterChromosome")
280     @XmlAccessorType(XmlAccessType.FIELD)
281     final static class Model {
282 
283         @XmlAttribute(name = "length", required = true)
284         public int length;
285 
286         @XmlElement(name = "valid-alleles", required = true, nillable = false)
287         public String validCharacters;
288 
289         @XmlElement(name = "alleles", required = true, nillable = false)
290         public String genes;
291 
292         public final static class Adapter
293             extends XmlAdapter<Model, CharacterChromosome>
294         {
295             @Override
296             public Model marshal(final CharacterChromosome value) {
297                 final Model m = new Model();
298                 m.length = value.length();
299                 m.validCharacters = value._validCharacters.toString();
300                 m.genes = value.toString();
301                 return m;
302             }
303 
304             @Override
305             public CharacterChromosome unmarshal(final Model m) {
306                 return CharacterChromosome.of(
307                     m.genes,
308                     new CharSeq(m.validCharacters)
309                 );
310             }
311         }
312     }
313 }