com.google.zetasql.DebugPrintableNode Maven / Gradle / Ivy
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.google.zetasql;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
/**
* Interface for pretty printing recursively defined tree-like structures.
*/
public interface DebugPrintableNode {
String nodeKindString();
/** Class used to collect all fields that should be printed in debugString. */
final class DebugStringField {
// If name is non-empty, "=" will be printed in front of the values.
private final String name;
// One of the two fields below will be filled in.
// If nodes is non-empty, all nodes are non-NULL.
private final String value; // Print this value directly.
private final ImmutableList extends DebugPrintableNode> nodes; // Print debugString for these
public DebugStringField(String name, String value) {
this.name = Preconditions.checkNotNull(name);
this.value = Preconditions.checkNotNull(value);
this.nodes = ImmutableList.of();
}
public DebugStringField(String name, ImmutableList extends DebugPrintableNode> nodes) {
this.name = Preconditions.checkNotNull(name);
this.value = null;
this.nodes = Preconditions.checkNotNull(nodes);
}
public DebugStringField(String name, DebugPrintableNode node) {
this.name = Preconditions.checkNotNull(name);
this.value = null;
this.nodes = ImmutableList.of(node);
}
public DebugStringField(String name, Object value) {
this.name = name;
this.value = value == null ? "" : value.toString();
this.nodes = ImmutableList.of();
}
public boolean hasNodes() {
return !nodes.isEmpty();
}
}
/**
* Print the tree recursively. The implementation is nearly identical to the C++ implementation in
* ResolvedNode::DebugStringImpl. The outputs need to be identical so the implementations should
* be kept similar to make changes to the output easier.
*
* @param prefix1 is the indentation to attach to child nodes.
* @param prefix2 is the indentation to attach to the root of this tree.
* @param sb is the output StringBuilder
*/
default void debugStringImpl(String prefix1, String prefix2, StringBuilder sb) {
List fields = new ArrayList<>();
collectDebugStringFields(fields);
// Use multiline DebugString format if any of the fields are Nodes.
boolean multiline = false;
for (DebugStringField field : fields) {
if (!field.nodes.isEmpty()) {
multiline = true;
break;
}
}
sb.append(prefix2).append(getNameForDebugString());
if (fields.isEmpty()) {
sb.append("\n");
} else if (multiline) {
sb.append("\n");
for (DebugStringField field : fields) {
boolean printFieldName = !field.name.isEmpty();
boolean hasNewlines = field.value != null && field.value.contains("\n");
boolean printOneLine = field.nodes.isEmpty() && !hasNewlines;
if (printFieldName) {
sb.append(prefix1).append("+-").append(field.name).append("=");
if (printOneLine) {
sb.append(field.value);
}
sb.append("\n");
} else if (printOneLine) {
sb.append(prefix1).append("+-").append(field.value).append("\n");
}
if (!printOneLine) {
if (hasNewlines) {
sb.append(prefix1).append("| \"\"\"\n");
for (String line : field.value.split("\n", /*limit=*/-1)) {
sb.append(prefix1).append("| ").append(line).append("\n");
}
sb.append(prefix1).append("| \"\"\"\n");
}
for (DebugPrintableNode node : field.nodes) {
Preconditions.checkState(node != null);
String fieldNameIndent =
printFieldName ? (field != fields.get(fields.size() - 1) ? "| " : " ") : "";
String fieldValueIndent = node != field.nodes.get(field.nodes.size() - 1) ? "| " : " ";
node.debugStringImpl(
prefix1 + fieldNameIndent + fieldValueIndent, prefix1 + fieldNameIndent + "+-", sb);
}
}
}
} else {
sb.append("(");
for (DebugStringField field : fields) {
if (field != fields.get(0)) {
sb.append(", ");
}
if (field.name.isEmpty()) {
sb.append(field.value);
} else {
sb.append(field.name).append("=").append(field.value);
}
}
sb.append(")\n");
}
}
/**
* Add all fields that should be printed in debugString to {@code fields}. Implementations should
* recursively call same method in the superclass.
*/
default void collectDebugStringFields(List fields) {};
/**
* Get the name string displayed for this node in the debugString. Normally it's just
* nodeKindString(), but it can be customized.
*/
default String getNameForDebugString() {
return nodeKindString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy