AbstractTreeGene.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.ext;
021 
022 import static java.lang.String.format;
023 import static java.util.Objects.requireNonNull;
024 import static io.jenetics.internal.util.Hashes.hash;
025 
026 import java.io.Serializable;
027 import java.util.Objects;
028 import java.util.Optional;
029 
030 import io.jenetics.util.ISeq;
031 
032 import io.jenetics.ext.util.Tree;
033 
034 /**
035  * Abstract implementation of the {@link TreeGene} interface..
036  *
037  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
038  @version 4.1
039  @since 3.9
040  */
041 public abstract class AbstractTreeGene<A, G extends AbstractTreeGene<A, G>>
042     implements TreeGene<A, G>, Serializable
043 {
044 
045     private static final long serialVersionUID = 1L;
046 
047     /**
048      * The allele of the tree-gene.
049      */
050     private final A _allele;
051     private final int _childOffset;
052     private final int _childCount;
053 
054     private ISeq<G> _genes;
055 
056     /**
057      * Creates a new tree-gene from the given data.
058      *
059      @param allele the actual value (allele) of the tree-gene
060      @param childOffset the offset index of the child in the containing
061      *        chromosome. If this node has no child, the value should be set
062      *        to zero.
063      @param childCount the number of children of this gene
064      @throws IllegalArgumentException if the {@code childCount} is smaller
065      *         than zero
066      */
067     protected AbstractTreeGene(
068         final A allele,
069         final int childOffset,
070         final int childCount
071     ) {
072         if (childCount < 0) {
073             throw new IllegalArgumentException(format(
074                 "Child count smaller than zero: %s", childCount
075             ));
076         }
077 
078         _allele = allele;
079         _childOffset = childOffset;
080         _childCount = childCount;
081     }
082 
083     /**
084      * Return the whole flattened tree values in breadth-first order. This method
085      * will always return the same {@code ISeq} instance.
086      *
087      @return the whole flattened tree values
088      */
089     @Override
090     public ISeq<G> flattenedNodes() {
091         return _genes;
092     }
093 
094     @Override
095     public G getRoot() {
096         return _genes.get(0);
097     }
098 
099     @Override
100     public boolean isRoot() {
101         return getRoot() == this;
102     }
103 
104     @Override
105     public int size() {
106         return isRoot() ? _genes.size() : TreeGene.super.size();
107     }
108 
109     protected void checkTreeState() {
110         if (_genes == null) {
111             throw new IllegalStateException(
112                 "Gene is not attached to a chromosome."
113             );
114         }
115     }
116 
117     /**
118      * This method is used by the {@code AbstractTreeChromosome} to attach
119      * itself to this gene.
120      *
121      @param genes the genes of the attached chromosome
122      */
123     protected void bind(final ISeq<G> genes) {
124         _genes = requireNonNull(genes);
125     }
126 
127     @Override
128     public int childOffset() {
129         return _childOffset;
130     }
131 
132     @Override
133     public A getAllele() {
134         return _allele;
135     }
136 
137     /**
138      * Return the <em>parent</em> node of this tree node.
139      *
140      @return the parent node, or {@code Optional.empty()} if this node is the
141      *         root of the tree
142      @throws IllegalStateException if this gene is not part of a chromosome
143      */
144     @Override
145     public Optional<G> getParent() {
146         checkTreeState();
147 
148         return _genes.stream()
149             .filter(g -> g.childStream().anyMatch(this::identical))
150             .findFirst();
151     }
152 
153     /**
154      * Return the child gene with the given index.
155      *
156      @param index the child index
157      @return the child node with the given index
158      @throws IndexOutOfBoundsException  if the {@code index} is out of
159      *         bounds ({@code [0, childCount())})
160      @throws IllegalStateException if this gene is not part of a chromosome
161      */
162     @Override
163     public G getChild(final int index) {
164         checkTreeState();
165         if (index < || index >= childCount()) {
166             throw new IndexOutOfBoundsException(format(
167                 "Child index out of bounds: %s", index
168             ));
169         }
170 
171         assert _genes != null;
172         return _genes.get(_childOffset + index);
173     }
174 
175     @Override
176     public int childCount() {
177         return _childCount;
178     }
179 
180     @Override
181     public boolean isValid() {
182         return _genes != null;
183     }
184 
185     @Override
186     public boolean identical(final Tree<?, ?> other) {
187         return other instanceof AbstractTreeGene &&
188             Objects.equals(((AbstractTreeGene)other)._allele, _allele&&
189             ((AbstractTreeGene)other)._genes == _genes &&
190             ((AbstractTreeGene)other)._childOffset == _childOffset &&
191             ((AbstractTreeGene)other)._childCount == _childCount;
192     }
193 
194     @Override
195     public int hashCode() {
196         return hash(_allele, hash(_childOffset, hash(_childCount)));
197     }
198 
199     @Override
200     public boolean equals(final Object obj) {
201         return obj == this ||
202             obj instanceof AbstractTreeGene &&
203             Objects.equals(((AbstractTreeGene)obj)._allele, _allele&&
204             ((AbstractTreeGene)obj)._childOffset == _childOffset &&
205             ((AbstractTreeGene)obj)._childCount == _childCount;
206     }
207 
208     @Override
209     public String toString() {
210         return Objects.toString(_allele);
211     }
212 
213 }