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 < 0 || 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(_allele) + 17;
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 }
|