CharacterGene.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-4.3.0).
003  * Copyright (c) 2007-2018 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@gmail.com)
019  */
020 package io.jenetics;
021 
022 import static java.util.Objects.requireNonNull;
023 import static io.jenetics.internal.util.Hashes.hash;
024 
025 import java.io.Serializable;
026 import java.util.Objects;
027 import java.util.Random;
028 
029 import io.jenetics.internal.math.random;
030 import io.jenetics.util.CharSeq;
031 import io.jenetics.util.ISeq;
032 import io.jenetics.util.IntRange;
033 import io.jenetics.util.MSeq;
034 import io.jenetics.util.RandomRegistry;
035 
036 /**
037  * Character gene implementation.
038  *
039  <p>This is a <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html">
040  * value-based</a> class; use of identity-sensitive operations (including
041  * reference equality ({@code ==}), identity hash code, or synchronization) on
042  * instances of {@code CharacterGene} may have unpredictable results and should
043  * be avoided.
044  *
045  @see CharacterChromosome
046  *
047  * @implNote
048  * This class is immutable and thread-safe.
049  *
050  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
051  @since 1.0
052  @version 4.3
053  */
054 public final class CharacterGene
055     implements
056         Gene<Character, CharacterGene>,
057         Comparable<CharacterGene>,
058         Serializable
059 {
060     private static final long serialVersionUID = 3L;
061 
062     /**
063      * The default character set used by this gene.
064      */
065     public static final CharSeq DEFAULT_CHARACTERS = new CharSeq(
066         "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +
067         " !\"$%&/()=?`{[]}\\+~*#';.:,-_<>|@^'"
068     );
069 
070     private final char _character;
071     private final CharSeq _validCharacters;
072 
073     private CharacterGene(final CharSeq chars, final int index) {
074         _character = chars.get(index);
075         _validCharacters = chars;
076     }
077 
078     /**
079      * Create a new character gene from the given {@code character} and the
080      * given set of valid characters.
081      *
082      @param character the char this gene represents
083      @param validChars the set of valid characters.
084      @throws NullPointerException if one of the arguments is {@code null}.
085      */
086     private CharacterGene(final char character, final CharSeq validChars) {
087         _character = character;
088         _validCharacters = requireNonNull(validChars);
089     }
090 
091     @Override
092     public boolean isValid() {
093         return _validCharacters.contains(_character);
094     }
095 
096     @Override
097     public Character getAllele() {
098         return _character;
099     }
100 
101     /**
102      * Return the {@code char} value of this character gene.
103      *
104      @return the {@code char} value.
105      */
106     public char charValue() {
107         return _character;
108     }
109 
110     /**
111      * Test, if the given character is valid.
112      *
113      @param character The character to test.
114      @return true if the character is valid, false otherwise.
115      */
116     public boolean isValidCharacter(final Character character) {
117         return _validCharacters.contains(character);
118     }
119 
120     /**
121      * Return a (unmodifiable) set of valid characters.
122      *
123      @return the {@link CharSeq} of valid characters.
124      */
125     public CharSeq getValidCharacters() {
126         return _validCharacters;
127     }
128 
129     /**
130      @see java.lang.Character#compareTo(java.lang.Character)
131      @param that The other gene to compare.
132      @return the value 0 if the argument Character is equal to this Character;
133      *         a value less than 0 if this Character is numerically less than
134      *         the Character argument; and a value greater than 0 if this
135      *         Character is numerically greater than the Character argument
136      *         (unsigned comparison). Note that this is strictly a numerical
137      *         comparison; it is not local-dependent.
138      */
139     @Override
140     public int compareTo(final CharacterGene that) {
141         return Character.compare(_character, that._character);
142     }
143 
144     @Override
145     public int hashCode() {
146         return hash(_character, hash(_validCharacters, hash(getClass())));
147     }
148 
149     @Override
150     public boolean equals(final Object obj) {
151         return obj == this ||
152             obj instanceof CharacterGene &&
153             ((CharacterGene)obj)._character == _character &&
154             Objects.equals(((CharacterGene)obj)._validCharacters, _validCharacters);
155     }
156 
157     @Override
158     public String toString() {
159         return Character.toString(_character);
160     }
161 
162 
163     /* *************************************************************************
164      *  Factory methods
165      * ************************************************************************/
166 
167     @Override
168     public CharacterGene newInstance() {
169         return of(_validCharacters);
170     }
171 
172     /**
173      * Create a new character gene from the given character. If the character
174      * is not within the {@link #getValidCharacters()}, an invalid gene will be
175      * created.
176      *
177      @param character the character value of the created gene.
178      @return a new character gene.
179      @throws NullPointerException if the given {@code character} is
180      *         {@code null}.
181      */
182     @Override
183     public CharacterGene newInstance(final Character character) {
184         return of(character, _validCharacters);
185     }
186 
187 
188     /* *************************************************************************
189      *  Static object creation methods
190      * ************************************************************************/
191 
192     /**
193      * Create a new CharacterGene with a randomly chosen character from the
194      * set of valid characters.
195      *
196      @param validCharacters the valid characters for this gene.
197      @return a new valid, <em>random</em> gene,
198      @throws NullPointerException if the {@code validCharacters} are
199      *         {@code null}.
200      */
201     public static CharacterGene of(final CharSeq validCharacters) {
202         return new CharacterGene(
203             validCharacters,
204             RandomRegistry.getRandom().nextInt(validCharacters.length())
205         );
206     }
207 
208     /**
209      * Create a new character gene from the given character. If the character
210      * is not within the {@link #DEFAULT_CHARACTERS}, an invalid gene will be
211      * created.
212      *
213      @param character the character value of the created gene.
214      @return a new character gene.
215      @throws NullPointerException if the given {@code character} is
216      *         {@code null}.
217      *
218      @deprecated Use {@link #of(char)} instead
219      */
220     @Deprecated
221     public static CharacterGene of(final Character character) {
222         return new CharacterGene(character, DEFAULT_CHARACTERS);
223     }
224 
225     /**
226      * Create a new character gene from the given character. If the character
227      * is not within the {@link #DEFAULT_CHARACTERS}, an invalid gene will be
228      * created.
229      *
230      @param character the character value of the created gene.
231      @return a new character gene.
232      */
233     public static CharacterGene of(final char character) {
234         return new CharacterGene(character, DEFAULT_CHARACTERS);
235     }
236 
237     /**
238      * Create a new random character gene, chosen from the
239      {@link #DEFAULT_CHARACTERS}.
240      *
241      @return a new random character gene.
242      */
243     public static CharacterGene of() {
244         return new CharacterGene(
245             DEFAULT_CHARACTERS,
246             RandomRegistry.getRandom().nextInt(DEFAULT_CHARACTERS.length())
247         );
248     }
249 
250     /**
251      * Create a new CharacterGene from the give character.
252      *
253      @param character The allele.
254      @param validCharacters the valid characters fo the new gene
255      @return a new {@code CharacterGene} with the given parameter
256      @throws NullPointerException if one of the arguments is {@code null}.
257      @throws IllegalArgumentException if the {@code validCharacters} are empty.
258      */
259     public static CharacterGene of(
260         final char character,
261         final CharSeq validCharacters
262     ) {
263         return new CharacterGene(character, validCharacters);
264     }
265 
266     static ISeq<CharacterGene> seq(
267         final CharSeq chars,
268         final IntRange lengthRange
269     ) {
270         final Random r = RandomRegistry.getRandom();
271 
272         return MSeq.<CharacterGene>ofLength(random.nextInt(lengthRange, r))
273             .fill(() -> new CharacterGene(chars, r.nextInt(chars.length())))
274             .toISeq();
275     }
276 
277 }