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