001 /*
002 * Java Genetic Algorithm Library (jenetics-7.1.2).
003 * Copyright (c) 2007-2023 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.util;
021
022 import static java.util.Objects.requireNonNull;
023
024 import java.util.ArrayList;
025 import java.util.HashMap;
026 import java.util.Iterator;
027 import java.util.List;
028 import java.util.Map;
029 import java.util.Objects;
030 import java.util.function.Function;
031 import java.util.stream.Collectors;
032
033 /**
034 * Definition of different tree formatter strategies.
035 *
036 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
037 * @version 5.0
038 * @since 5.0
039 */
040 public abstract class TreeFormatter {
041
042 /**
043 * Formats a given tree to a <em>tree</em> string representation.
044 * <pre>
045 * mul
046 * ├── div
047 * │ ├── cos
048 * │ │ └── 1.0
049 * │ └── cos
050 * │ └── π
051 * └── sin
052 * └── mul
053 * ├── 1.0
054 * └── z
055 * </pre>
056 */
057 public static final TreeFormatter TREE = new TreeFormatter() {
058
059 @Override
060 public <V> String format(
061 final Tree<V, ?> tree,
062 final Function<? super V, ? extends CharSequence> mapper
063 ) {
064 requireNonNull(tree);
065 requireNonNull(mapper);
066
067 return toStrings(tree, mapper).stream()
068 .map(StringBuilder::toString)
069 .collect(Collectors.joining("\n"));
070 }
071
072 private <V> List<StringBuilder> toStrings(
073 final Tree<V, ?> tree,
074 final Function<? super V, ? extends CharSequence> mapper
075 ) {
076 final List<StringBuilder> result = new ArrayList<>();
077 result.add(new StringBuilder().append(mapper.apply(tree.value())));
078
079 final Iterator<? extends Tree<V, ?>> it = tree.childIterator();
080 while (it.hasNext()) {
081 final List<StringBuilder> subtree = toStrings(it.next(), mapper);
082 if (it.hasNext()) {
083 subtree(result, subtree);
084 } else {
085 lastSubtree(result, subtree);
086 }
087 }
088 return result;
089 }
090
091 private <V> void subtree(
092 final List<StringBuilder> result,
093 final List<StringBuilder> subtree
094 ) {
095 final Iterator<StringBuilder> it = subtree.iterator();
096 result.add(it.next().insert(0, "├── "));
097 while (it.hasNext()) {
098 result.add(it.next().insert(0, "│ "));
099 }
100 }
101
102 private void lastSubtree(
103 final List<StringBuilder> result,
104 final List<StringBuilder> subtree
105 ) {
106 final Iterator<StringBuilder> it = subtree.iterator();
107 result.add(it.next().insert(0, "└── "));
108 while (it.hasNext()) {
109 result.add(it.next().insert(0, " "));
110 }
111 }
112 };
113
114 /**
115 * Formats a given tree to a parentheses string representation.
116 * <pre>
117 * mul(div(cos(1.0),cos(π)),sin(mul(1.0,z)))
118 * </pre>
119 */
120 public static final TreeFormatter PARENTHESES = new TreeFormatter() {
121 @Override
122 public <V> String format(
123 final Tree<V, ?> tree,
124 final Function<? super V, ? extends CharSequence> mapper
125 ) {
126 requireNonNull(tree);
127 requireNonNull(mapper);
128 return ParenthesesTrees.toString(tree, mapper);
129 }
130 };
131
132 /**
133 * Formats a given tree to a lisp string representation.
134 * <pre>
135 * (mul (div (cos 1.0) (cos π)) (sin (mul 1.0 z)))
136 * </pre>
137 */
138 public static final TreeFormatter LISP = new TreeFormatter() {
139 @Override
140 public <V> String format(
141 final Tree<V, ?> tree,
142 final Function<? super V, ? extends CharSequence> mapper
143 ) {
144 final CharSequence value = mapper.apply(tree.value());
145 if (tree.isLeaf()) {
146 return value.toString();
147 } else {
148 final String children = tree.childStream()
149 .map(child -> format(child, mapper))
150 .collect(Collectors.joining(" "));
151 return "(" + value + " " + children + ")";
152 }
153 }
154 };
155
156 /**
157 * A tree formatter for .dot string representations. This strings can be
158 * used to create nice looking tree images. The tree
159 * <pre>
160 * mul(div(cos(1.0),cos(π)),sin(mul(1.0,z)))
161 * </pre>
162 * is rendered into this dot string
163 * <pre>
164 * digraph Tree {
165 * node_001 [label="div"];
166 * node_002 [label="cos"];
167 * node_003 [label="1.0"];
168 * node_004 [label="cos"];
169 * node_000 [label="mul"];
170 * node_009 [label="z"];
171 * node_005 [label="π"];
172 * node_006 [label="sin"];
173 * node_007 [label="mul"];
174 * node_008 [label="1.0"];
175 * node_000 -> node_001;
176 * node_001 -> node_002;
177 * node_002 -> node_003;
178 * node_001 -> node_004;
179 * node_004 -> node_005;
180 * node_000 -> node_006;
181 * node_006 -> node_007;
182 * node_007 -> node_008;
183 * node_007 -> node_009;
184 * }
185 * </pre>
186 * This dot string can be rendered into the following graph:
187 * <p>
188 * <img alt="Dot-tree" src="doc-files/dot-tree.svg" width="400" height="252" >
189 * </p>
190 */
191 public static final TreeFormatter DOT = dot("Tree");
192
193 protected TreeFormatter() {
194 }
195
196 /**
197 * Formats the given {@code tree} to its string representation. The given
198 * {@code mapper} is used for converting the node type {@code V} to a string
199 * value.
200 *
201 * @param tree the input tree to format
202 * @param mapper the tree node value mapper
203 * @param <V> the tree node type
204 * @return the string representation of the given {@code tree}
205 * @throws NullPointerException if one of the arguments is {@code null}
206 */
207 public abstract <V> String format(
208 final Tree<V, ?> tree,
209 final Function<? super V, ? extends CharSequence> mapper
210 );
211
212 /**
213 * Formats the given {@code tree} to its string representation.
214 *
215 * @param tree the input tree to format
216 * @return the string representation of the given {@code tree}
217 * @throws NullPointerException if the {@code tree} is {@code null}
218 */
219 public String format(final Tree<?, ?> tree) {
220 return format(tree, Objects::toString);
221 }
222
223 /**
224 * A tree formatter for .dot string representations. This strings can be
225 * used to create nice looking tree images. The tree
226 * <pre>
227 * mul(div(cos(1.0),cos(π)),sin(mul(1.0,z)))
228 * </pre>
229 * is rendered into this dot string
230 * <pre>
231 * digraph Tree {
232 * node_001 [label="div"];
233 * node_002 [label="cos"];
234 * node_003 [label="1.0"];
235 * node_004 [label="cos"];
236 * node_000 [label="mul"];
237 * node_009 [label="z"];
238 * node_005 [label="π"];
239 * node_006 [label="sin"];
240 * node_007 [label="mul"];
241 * node_008 [label="1.0"];
242 * node_000 -> node_001;
243 * node_001 -> node_002;
244 * node_002 -> node_003;
245 * node_001 -> node_004;
246 * node_004 -> node_005;
247 * node_000 -> node_006;
248 * node_006 -> node_007;
249 * node_007 -> node_008;
250 * node_007 -> node_009;
251 * }
252 * </pre>
253 * This dot string can be rendered into the following graph:
254 * <p>
255 * <img alt="Dot-tree" src="doc-files/dot-tree.svg" width="400" height="252" >
256 * </p>
257 *
258 * @param treeName the name of the digraph
259 * @return a dot string formatter
260 * @throws NullPointerException if the given tree name is {@code null}
261 */
262 public static TreeFormatter dot(final String treeName) {
263 return new Dotty(treeName);
264 }
265
266
267 /* *************************************************************************
268 * Some helper classes.
269 * ************************************************************************/
270
271 /**
272 * This formatter converts a tree to the .dot representation.
273 */
274 private static final class Dotty extends TreeFormatter {
275 private final String _name;
276
277 Dotty(final String name) {
278 _name = requireNonNull(name);
279 }
280
281 @Override
282 public <V> String format(
283 final Tree<V, ?> tree,
284 final Function<? super V, ? extends CharSequence> mapper
285 ) {
286 return new Helper<>(_name, tree, mapper).draw();
287 }
288
289 private static final class Helper<V> {
290 private final String _name;
291 private final Function<? super V, ? extends CharSequence> _mapper;
292
293 private final Map<String, CharSequence> _labels = new HashMap<>();
294 private final List<String> _edges = new ArrayList<>();
295
296 Helper(
297 final String name,
298 final Tree<V, ?> tree,
299 final Function<? super V, ? extends CharSequence> mapper
300 ) {
301 _name = requireNonNull(name);
302 _mapper = requireNonNull(mapper);
303 init(tree, null, 0);
304 }
305
306 private int init(
307 final Tree<V, ?> tree,
308 final String parentLabel,
309 final int index
310 ) {
311 int idx = index;
312 final CharSequence value = _mapper.apply(tree.value());
313 final String label = String.format("node_%03d", idx);
314 _labels.put(label, value);
315
316 if (parentLabel != null) {
317 _edges.add(parentLabel + " -> " + label);
318 }
319 for (int i = 0; i < tree.childCount(); ++i) {
320 final Tree<V, ?> child = tree.childAt(i);
321 idx = init(child, label, idx + 1);
322 }
323 return idx;
324 }
325
326 String draw() {
327 final StringBuilder builder = new StringBuilder();
328 builder
329 .append("digraph ")
330 .append(_name)
331 .append(" {\n");
332
333 _labels.forEach((key, value) ->
334 builder
335 .append(" ")
336 .append(key)
337 .append(" [label=\"")
338 .append(value.toString().replace("\"", "\\\""))
339 .append("\"];\n")
340 );
341
342 _edges.forEach(edge ->
343 builder
344 .append(" ")
345 .append(edge)
346 .append(";\n")
347 );
348 builder.append("}\n");
349
350 return builder.toString();
351 }
352 }
353 }
354
355 }
|