013ca97e833bd281e88767619a6b7a4dd9e74717
[yangtools.git] / data / yang-data-spi / src / main / java / org / opendaylight / yangtools / yang / data / spi / node / NormalizedNodePrettyTree.java
1 /*
2  * Copyright (c) 2021 PANTHEON.tech s.r.o. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.spi.node;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import java.util.Base64;
14 import java.util.Iterator;
15 import java.util.Locale;
16 import java.util.Optional;
17 import org.eclipse.jdt.annotation.NonNull;
18 import org.opendaylight.yangtools.concepts.Immutable;
19 import org.opendaylight.yangtools.concepts.PrettyTree;
20 import org.opendaylight.yangtools.concepts.PrettyTreeAware;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.common.QNameModule;
23 import org.opendaylight.yangtools.yang.common.Revision;
24 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
25 import org.opendaylight.yangtools.yang.data.api.schema.ForeignDataNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
28 import org.opendaylight.yangtools.yang.data.api.schema.ValueNode;
29
30 @Beta
31 public final class NormalizedNodePrettyTree extends PrettyTree implements Immutable {
32     private final @NonNull NormalizedNode node;
33
34     public NormalizedNodePrettyTree(final @NonNull NormalizedNode node) {
35         this.node = requireNonNull(node);
36     }
37
38     @Override
39     public void appendTo(final StringBuilder sb, final int depth) {
40         appendNode(sb, depth, null, node);
41     }
42
43     private static void appendNode(final StringBuilder sb, final int depth, final QNameModule parentNamespace,
44             final NormalizedNode node) {
45         final String simpleName = node.contract().getSimpleName();
46         appendIndent(sb, depth);
47         sb.append(simpleName.toLowerCase(Locale.ROOT).charAt(0)).append(simpleName, 1, simpleName.length()).append(' ');
48
49         final QNameModule currentNamespace;
50         if (node instanceof AugmentationNode) {
51             // Add identifier, but augmentations are special enough
52             currentNamespace = ((AugmentationNode) node).getIdentifier().getPossibleChildNames().iterator().next()
53                 .getModule();
54             if (appendNamespace(sb, parentNamespace, currentNamespace)) {
55                 sb.append(' ');
56             }
57         } else {
58             final QName qname = node.getIdentifier().getNodeType();
59             currentNamespace = qname.getModule();
60             appendNamespace(sb, parentNamespace, currentNamespace);
61             sb.append(qname.getLocalName()).append(' ');
62         }
63
64         if (node instanceof NormalizedNodeContainer) {
65             final NormalizedNodeContainer<?> container = (NormalizedNodeContainer<?>) node;
66             sb.append("= {");
67
68             final Iterator<? extends NormalizedNode> it = container.body().iterator();
69             if (it.hasNext()) {
70                 final int childIndent = depth + 1;
71                 do {
72                     sb.append('\n');
73                     appendNode(sb, childIndent, currentNamespace, it.next());
74                 } while (it.hasNext());
75
76                 sb.append('\n');
77                 appendIndent(sb, depth);
78             }
79             sb.append('}');
80         } else if (node instanceof ValueNode) {
81             sb.append("= ");
82             final Object value = node.body();
83             if (value instanceof byte[]) {
84                 sb.append("(byte[])").append(Base64.getEncoder().encodeToString((byte[]) value));
85             } else if (value instanceof String) {
86                 appendString(sb, (String) value);
87             } else {
88                 sb.append(value);
89             }
90         } else if (node instanceof ForeignDataNode) {
91             final ForeignDataNode<?> data = (ForeignDataNode<?>) node;
92             final Object body = data.body();
93             if (body instanceof PrettyTreeAware) {
94                 sb.append("= {\n");
95                 ((PrettyTreeAware) body).prettyTree().appendTo(sb, depth + 1);
96                 appendIndent(sb, depth);
97                 sb.append('}');
98             } else {
99                 sb.append("= (").append(data.bodyObjectModel().getName()).append(')');
100             }
101         } else {
102             throw new IllegalStateException("Unhandled node " + node);
103         }
104     }
105
106     private static boolean appendNamespace(final StringBuilder sb, final QNameModule parent,
107             final QNameModule current) {
108         if (!current.equals(parent)) {
109             sb.append('(').append(current.getNamespace());
110             final Optional<Revision> rev = current.getRevision();
111             if (rev.isPresent()) {
112                 sb.append('@').append(rev.orElseThrow());
113             }
114             sb.append(')');
115             return true;
116         }
117         return false;
118     }
119
120     private static void appendString(final StringBuilder sb, final String str) {
121         // TODO: do some escaping: '\r' '\n' '"' '\\' to make things even more zazzy
122         sb.append('"').append(str).append('"');
123     }
124 }