| /******************************************************************************* |
| * Copyright (c) 2000, 2012 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdi.internal; |
| |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| |
| public class VerboseWriter { |
| /** Length of verbose description. */ |
| public static final int VERBOSE_DESCRIPTION_LENGTH = 21; |
| /** Number of hexadecimal verbose bytes per line. */ |
| public static final int VERBOSE_HEX_BYTES_PER_LINE = 16; |
| /** Width of hex dump. */ |
| public static final int VERBOSE_HEX_WIDTH = 16 * 3 + 2; |
| |
| /** |
| * Number extra verbose lines. These are caused by hex dumps that span more |
| * than one line. |
| */ |
| int fExtraVerboseLines = 0; |
| |
| /** PrintWriter that is written to. */ |
| private PrintWriter fOutput; |
| /** Buffer for output: one StringBuilder entry per line. */ |
| private List<StringBuilder> fLineBuffer; |
| /** Position from where buffer is written to. */ |
| private int fPosition; |
| /** True if the current line has not yet been written to. */ |
| private boolean fNewLine = true; |
| |
| /** |
| * Creates new VerboseWriter that writes to the given PrintWriter. Output is |
| * buffered and previous entries in the buffer can be rewritten. |
| */ |
| public VerboseWriter(PrintWriter out) { |
| fOutput = out; |
| fLineBuffer = new ArrayList<>(); |
| fPosition = 0; |
| fLineBuffer.add(new StringBuilder()); |
| } |
| |
| /** |
| * Terminate the current line by writing the line separator string. If |
| * autoflush is set and there are extra vebose lines caused by printHex, |
| * these lines are also printed. |
| */ |
| public void println() { |
| while (fExtraVerboseLines > 0) { |
| fExtraVerboseLines--; |
| markLn(); |
| } |
| |
| markLn(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, byte value) { |
| printDescription(description); |
| printHex(value); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, short value) { |
| printDescription(description); |
| printHex(value); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, int value) { |
| printDescription(description); |
| printHex(value); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, long value) { |
| printDescription(description); |
| printHex(value); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, byte value, Map<Integer, String> valueToString) { |
| printDescription(description); |
| printHex(value); |
| printValue(value, valueToString); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, short value, Map<Integer, String> valueToString) { |
| printDescription(description); |
| printHex(value); |
| printValue(value, valueToString); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, int value, Map<Integer, String> valueToString) { |
| printDescription(description); |
| printHex(value); |
| printValue(value, valueToString); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, byte value, String[] bitNames) { |
| printDescription(description); |
| printHex(value); |
| printValue(value, bitNames); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, short value, String[] bitNames) { |
| printDescription(description); |
| printHex(value); |
| printValue(value, bitNames); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, int value, String[] bitNames) { |
| printDescription(description); |
| printHex(value); |
| printValue(value, bitNames); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, String value) { |
| printDescription(description); |
| printHex(value); |
| print(value); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, boolean value) { |
| printDescription(description); |
| printHex(value); |
| print(Boolean.valueOf(value).toString()); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, char value) { |
| printDescription(description); |
| printHex(value); |
| print(value); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, double value) { |
| printDescription(description); |
| printHex(value); |
| print(new Double(value).toString()); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, float value) { |
| printDescription(description); |
| printHex(value); |
| print(new Float(value).toString()); |
| println(); |
| } |
| |
| /** |
| * Prints verbose line. |
| */ |
| public void println(String description, byte[] value) { |
| printDescription(description); |
| printHex(value); |
| println(); |
| } |
| |
| /** |
| * Prints string with right size. |
| */ |
| public void printWidth(String str, int width) { |
| print(str); |
| int spaces = width - str.length(); |
| if (spaces > 0) { |
| for (int i = 0; i < spaces; i++) { |
| print(' '); |
| } |
| } |
| } |
| |
| /** |
| * Prints description string with right size plus its seperator spaces. |
| */ |
| public void printDescription(String str) { |
| printWidth(str, VERBOSE_DESCRIPTION_LENGTH); |
| } |
| |
| /** |
| * Prints hex substitution string with right size plus its seperator spaces. |
| */ |
| public void printHexSubstitution(String str) { |
| // Note that bytes also start with a space. |
| print(' '); |
| printWidth(str, VERBOSE_HEX_WIDTH - 1); |
| } |
| |
| /** |
| * Appends hex representation of given byte to an array. |
| */ |
| private static void appendHexByte(byte b, char[] buffer, int pos) { |
| int count = 2; |
| |
| int abspos = 3 * pos; |
| buffer[abspos] = ' '; |
| do { |
| int t = b & 15; |
| if (t > 9) { |
| t = t - 10 + 'a'; |
| } else { |
| t += '0'; |
| } |
| buffer[count-- + abspos] = (char) t; |
| b >>>= 4; |
| } while (count > 0); |
| } |
| |
| /** |
| * Appends remaining spaces to hex dump. |
| */ |
| private static void appendHexSpaces(char[] buffer, int pos) { |
| for (int i = 3 * pos; i <= VERBOSE_HEX_WIDTH - 3; i += 3) { |
| buffer[i] = ' '; |
| buffer[i + 1] = ' '; |
| buffer[i + 2] = ' '; |
| } |
| |
| // Two extra spaces as seperator |
| buffer[VERBOSE_HEX_WIDTH - 1] = ' '; |
| buffer[VERBOSE_HEX_WIDTH - 2] = ' '; |
| } |
| |
| /** |
| * Prints hex representation of a byte. |
| */ |
| public void printHex(byte b) { |
| char buffer[] = new char[VERBOSE_HEX_WIDTH]; |
| appendHexByte(b, buffer, 0); |
| appendHexSpaces(buffer, 1); |
| print(buffer); |
| } |
| |
| /** |
| * Prints hex representation of an int. |
| */ |
| public void printHex(short s) { |
| char buffer[] = new char[VERBOSE_HEX_WIDTH]; |
| for (int i = 1; i >= 0; i--) |
| appendHexByte((byte) (s >>> i * 8), buffer, 1 - i); |
| appendHexSpaces(buffer, 2); |
| print(buffer); |
| } |
| |
| /** |
| * Prints hex representation of an int. |
| */ |
| public void printHex(int integer) { |
| char buffer[] = new char[VERBOSE_HEX_WIDTH]; |
| for (int i = 3; i >= 0; i--) |
| appendHexByte((byte) (integer >>> i * 8), buffer, 3 - i); |
| appendHexSpaces(buffer, 4); |
| print(buffer); |
| } |
| |
| /** |
| * Prints hex representation of a long. |
| */ |
| public void printHex(long l) { |
| char buffer[] = new char[VERBOSE_HEX_WIDTH]; |
| for (int i = 7; i >= 0; i--) |
| appendHexByte((byte) (l >>> i * 8), buffer, 7 - i); |
| appendHexSpaces(buffer, 8); |
| print(buffer); |
| } |
| |
| /** |
| * Prints hex representation of a long. |
| * @param b the boolean |
| */ |
| public void printHex(boolean b) { |
| printHexSubstitution("<boolean>"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Prints hex representation of a long. |
| * @param c the char |
| */ |
| public void printHex(char c) { |
| printHexSubstitution("<char>"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Prints hex representation of a long. |
| * @param d the double |
| */ |
| public void printHex(double d) { |
| printHexSubstitution("<double>"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Prints hex representation of a long. |
| * @param f the float |
| */ |
| public void printHex(float f) { |
| printHexSubstitution("<float>"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Prints hex representation of a String. |
| * @param str the string |
| */ |
| public void printHex(String str) { |
| printHexSubstitution("<string>"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Prints hex representation of a byte array. Note that this can span more |
| * than one line, but is considered to be part of one 'verbose line'. |
| * Therefore, a println after a printHex can result in more than one line |
| * being printed to the PrintWriter. |
| */ |
| public void printHex(byte[] bytes) { |
| int startPosition = position(); |
| char linebuf[] = new char[VERBOSE_HEX_WIDTH]; |
| int extraLines = 0; |
| int byteOnLine = 0; |
| |
| for (byte b : bytes) { |
| if (byteOnLine == VERBOSE_HEX_BYTES_PER_LINE) { |
| appendHexSpaces(linebuf, VERBOSE_HEX_BYTES_PER_LINE); |
| if (extraLines++ > 0) { |
| printDescription(""); //$NON-NLS-1$ |
| } |
| print(linebuf); |
| markLn(); |
| byteOnLine = 0; |
| } |
| appendHexByte(b, linebuf, byteOnLine++); |
| } |
| appendHexSpaces(linebuf, byteOnLine); |
| if (extraLines > 0) { |
| printDescription(""); //$NON-NLS-1$ |
| } |
| |
| fExtraVerboseLines += extraLines; |
| print(linebuf); |
| if (extraLines > 0) { |
| gotoPosition(startPosition); |
| } |
| } |
| |
| /** |
| * Prints string representation of a value given a Map from values to |
| * strings. |
| */ |
| public void printValue(int value, Map<Integer, String> valueToString) { |
| Integer val = new Integer(value); |
| if (valueToString == null) { |
| print(val.toString()); |
| return; |
| } |
| String result = valueToString.get(val); |
| if (result == null) { |
| print(val.toString() + JDIMessages.VerboseWriter___unknown_value__1); |
| } else { |
| print(result); |
| } |
| } |
| |
| /** |
| * Prints string representation of a value given a Vector with the names of |
| * the bits. |
| */ |
| public void printValue(byte value, String[] bitNames) { |
| printValue(value & 0xff, bitNames); |
| } |
| |
| /** |
| * Prints string representation of a value given a Vector with the names of |
| * the bits. |
| */ |
| public void printValue(short value, String[] bitNames) { |
| printValue(value & 0xffff, bitNames); |
| } |
| |
| /** |
| * Prints string representation of a value given a Vector with the names of |
| * the bits. |
| */ |
| public void printValue(int value, String[] bitNames) { |
| Integer val = new Integer(value); |
| if (bitNames == null) { |
| print(val.toString()); |
| return; |
| } |
| |
| boolean bitsSet = false; |
| |
| for (int i = 0; i < bitNames.length; i++) { |
| // Test if bit is set in value. |
| if ((1 << i & value) == 0) { |
| continue; |
| } |
| |
| // See if we have a desciption for the bit. |
| String bitString = bitNames[i]; |
| if (bitString == null) { |
| bitString = JDIMessages.VerboseWriter__unknown_bit__2; |
| } |
| |
| if (!bitsSet) { |
| print(bitString); |
| } else { |
| print(" & "); //$NON-NLS-1$ |
| print(bitString); |
| } |
| bitsSet = true; |
| } |
| |
| if (!bitsSet) { |
| print(JDIMessages.VerboseWriter__none__4); |
| } |
| } |
| |
| /** |
| * Checks if a new line is written to. If so, first erase any data on that |
| * line. Line is marked 'not new' after this command. |
| */ |
| private void checkForNewLine() { |
| if (fNewLine) { |
| (fLineBuffer.get(fPosition)).setLength(0); |
| fNewLine = false; |
| } |
| } |
| |
| /** |
| * Print a String. |
| */ |
| public void print(String str) { |
| checkForNewLine(); |
| (fLineBuffer.get(fPosition)).append(str); |
| } |
| |
| /** |
| * Print a Character. |
| */ |
| public void print(char c) { |
| checkForNewLine(); |
| (fLineBuffer.get(fPosition)).append(c); |
| } |
| |
| /** |
| * Print array of Characters. |
| */ |
| public void print(char[] c) { |
| checkForNewLine(); |
| (fLineBuffer.get(fPosition)).append(c); |
| } |
| |
| /** |
| * Print a String and then terminate the line. |
| */ |
| public void println(String str) { |
| print(str); |
| println(); |
| } |
| |
| /** |
| * Flush buffer. If autoflush is off, this method is synchronized on the |
| * PrintWriter given in the constructor. |
| */ |
| public void flush() { |
| synchronized (fOutput) { |
| int bufSize = fLineBuffer.size(); |
| |
| for (int i = 0; i < bufSize - 1; i++) |
| fOutput.println(new String(fLineBuffer.get(i))); |
| |
| // The last line should be printed without an extra newline |
| StringBuilder lastLine = fLineBuffer.get(bufSize - 1); |
| if (lastLine.length() > 0) |
| fOutput.print(new String(lastLine)); |
| |
| fOutput.flush(); |
| fLineBuffer.clear(); |
| fPosition = 0; |
| fLineBuffer.add(new StringBuilder()); |
| } |
| } |
| |
| /** |
| * Go to the given position in the buffer. If the given position is smaller |
| * than the current position, subsequent print commands overwrite existing |
| * lines in the buffer. Else, new lines are added to the buffer. |
| */ |
| public void gotoPosition(int pos) { |
| int delta = pos - fPosition; |
| if (delta < 0) { |
| fPosition = pos; |
| } else { |
| while (delta-- > 0) |
| println(); |
| } |
| } |
| |
| /** |
| * Prints given number of lines. |
| */ |
| public void printLines(int lines) { |
| gotoPosition(fPosition + lines); |
| } |
| |
| /** |
| * @return Returns current position in buffer. |
| */ |
| public int position() { |
| return fPosition; |
| } |
| |
| /** |
| * Terminate the current line by writing the line separator string, start at |
| * end of next line. |
| */ |
| public void markLn() { |
| if (++fPosition == fLineBuffer.size()) { |
| fLineBuffer.add(new StringBuilder()); |
| } |
| |
| fNewLine = true; |
| } |
| } |