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