blob: 31814e19e0d8d3a4dc99f777c82c7fbfd90d8cf0 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2014, 2020 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ltk.ast.core.util;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.ltk.ast.core.AstNode;
import org.eclipse.statet.ltk.ast.core.AstVisitor;
@NonNullByDefault
public class AstPrinter implements AstVisitor {
protected final Writer writer;
private @Nullable String text;
private int level;
private final int maxFragmentSize= 25;
public AstPrinter(final Writer writer) {
this.writer= writer;
}
public Writer getWriter() {
return this.writer;
}
public void print(final AstNode node, final String text) throws IOException {
try {
this.level= 0;
this.text= text;
node.accept(this);
}
catch (final InvocationTargetException e) {
throw (IOException) e.getCause();
}
finally {
this.text= null;
}
}
@Override
public void visit(final AstNode node) throws InvocationTargetException {
try {
printIdent(this.level);
this.writer.append('[');
this.writer.append(Integer.toString(node.getStartOffset()));
this.writer.append(", "); //$NON-NLS-1$
this.writer.append(Integer.toString(node.getEndOffset()));
this.writer.append(") "); //$NON-NLS-1$
printNodeInfo(node);
printFragment(node.getStartOffset(), node.getEndOffset());
this.writer.append('\n');
this.level++;
node.acceptInChildren(this);
this.level--;
}
catch (final IOException e) {
throw new InvocationTargetException(e);
}
}
protected void printIdent(final int depth) throws IOException {
for (int i= 0; i < depth; i++) {
this.writer.write(" "); //$NON-NLS-1$
}
}
protected void printNodeInfo(final AstNode node) throws IOException {
this.writer.append(node.getClass().getSimpleName());
}
protected void printFragment(final int startOffset, final int endOffset) throws IOException {
final String text= this.text;
if (text != null && this.maxFragmentSize > 0) {
this.writer.write(": "); //$NON-NLS-1$
int l= endOffset - startOffset;
if (l <= this.maxFragmentSize) {
writeEncoded(text, startOffset, l);
}
else if (this.maxFragmentSize < 13) {
writeEncoded(text, startOffset, this.maxFragmentSize - 3);
this.writer.write(" ... "); //$NON-NLS-1$
}
else {
l= (this.maxFragmentSize - 3) / 2;
writeEncoded(text, startOffset, l);
this.writer.write(" ... "); //$NON-NLS-1$
writeEncoded(text, endOffset - l, l);
}
}
}
private void writeEncoded(final String s, final int begin, final int length) throws IOException {
final int end= begin + length;
for (int i= begin; i < end; i++) {
if (i < 0) {
this.writer.write("<E: out of bounds>");
i= 0;
}
else if (i >= s.length()) {
this.writer.write("<E: out of bounds>");
return;
}
final int c= s.charAt(i);
if (c < 0x10) {
this.writer.write("<0x0"); //$NON-NLS-1$
this.writer.write(Integer.toHexString(c));
this.writer.write('>');
}
else if (c < 0x20) {
this.writer.write("<0x"); //$NON-NLS-1$
this.writer.write(Integer.toHexString(c));
this.writer.write('>');
}
else {
this.writer.write(c);
}
}
}
}