/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Copyright (c) 2004-2005, Regents of the University of California
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the University of California, Los Angeles nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.oracle.max.elf;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
/**
* The <code>StringUtil</code> class implements several useful functions for dealing with strings such as
* parsing pieces of syntax, formatting, etc.
*/
public final class StringUtil {
public static final String QUOTE = "\"";
public static final String SQUOTE = "'";
public static final String LPAREN = "(";
public static final String RPAREN = ")";
public static final String COMMA = ",";
public static final String COMMA_SPACE = ", ";
public static final String[] EMPTY_STRING_ARRAY = {};
public static final char SQUOTE_CHAR = '\'';
public static final char BACKSLASH = '\\';
public static final char QUOTE_CHAR = '"';
private StringUtil() {
}
/**
* The <code>addToString()</code> method converts a numerical address (represented as a signed 32-bit
* integer) and converts it to a string in the format 0xXXXX where 'X' represents a hexadecimal character.
* The address is assumed to fit in 4 hexadecimal characters. If it does not, the string will have as many
* characters as necessary (max 8) to represent the address.
*
* @param address the address value as an integer
* @return a standard string representation of the address
*/
public static String addrToString(int address) {
return to0xHex(address, 4);
}
public static int readHexValue(CharacterIterator i, int maxchars) {
int accumul = 0;
for (int cntr = 0; cntr < maxchars; cntr++) {
final char c = i.current();
if (c == CharacterIterator.DONE || !isHexDigit(c)) {
break;
}
accumul = (accumul << 4) | hexValueOf(c);
i.next();
}
return accumul;
}
public static int readOctalValue(CharacterIterator i, int maxchars) {
int accumul = 0;
for (int cntr = 0; cntr < maxchars; cntr++) {
final char c = i.current();
if (!isOctalDigit(c)) {
break;
}
accumul = (accumul << 3) | octalValueOf(c);
i.next();
}
return accumul;
}
public static int readBinaryValue(CharacterIterator i, int maxchars) {
int accumul = 0;
if (maxchars >= 1) {
final char ch = i.current();
i.next();
if (ch == '0') {
accumul <<= 1;
} else if (ch == '1') {
accumul = (accumul << 1) | 1;
}
}
return accumul;
}
public static int readDecimalValue(CharacterIterator i, int maxchars) {
final StringBuffer buf = new StringBuffer();
if (peekAndEat(i, '-')) {
buf.append('-');
}
for (int cntr = 0; cntr < maxchars; cntr++) {
final char c = i.current();
if (!Character.isDigit(c)) {
break;
}
buf.append(c);
i.next();
}
return Integer.parseInt(buf.toString());
}
public static String readDecimalString(CharacterIterator i, int maxchars) {
final StringBuffer buf = new StringBuffer();
if (peekAndEat(i, '-')) {
buf.append('-');
}
for (int cntr = 0; cntr < maxchars; cntr++) {
final char c = i.current();
if (!Character.isDigit(c)) {
break;
}
buf.append(c);
i.next();
}
return buf.toString();
}
public static int readIntegerValue(CharacterIterator i) {
char ch = i.current();
if (ch == '-') {
return readDecimalValue(i, 10);
}
if (ch == '0') {
ch = i.next();
if (ch == 'x' || ch == 'X') {
i.next();
return readHexValue(i, 8);
} else if (ch == 'b' || ch == 'B') {
i.next();
return readBinaryValue(i, 32);
} else {
return readOctalValue(i, 11);
}
}
return readDecimalValue(i, 10);
}
public static void skipWhiteSpace(CharacterIterator i) {
while (true) {
final char c = i.current();
if (c != ' ' && c != '\n' && c != '\t') {
break;
}
i.next();
}
}
public static char peek(CharacterIterator i) {
return i.current();
}
public static boolean peekAndEat(CharacterIterator i, char c) {
final char r = i.current();
if (r == c) {
i.next();
return true;
}
return false;
}
public static boolean peekAndEat(CharacterIterator i, String s) {
final int ind = i.getIndex();
for (int cntr = 0; cntr < s.length(); cntr++) {
if (i.current() == s.charAt(cntr)) {
i.next();
} else {
i.setIndex(ind);
return false;
}
}
return true;
}
public static void expectChar(CharacterIterator i, char c) throws Exception {
final char r = i.current();
i.next();
if (r != c) {
throw new Error("parse error at " + i.getIndex() + ", expected character '" + c + "'");
}
}
public static void expectChars(CharacterIterator i, String s) throws Exception {
for (int cntr = 0; cntr < s.length(); cntr++) {
expectChar(i, s.charAt(cntr));
}
}
/**
* The <code>isHex()</code> method checks whether the specifed string represents a hexadecimal
* integer. This method only checks the first two characters. If they match "0x" or "0X", then
* this method returns true, otherwise, it returns false.
*
* @param s the string to check whether it begins with a hexadecimal sequence
* @return true if the string begins with "0x" or "0X"; false otherwise
*/
public static boolean isHex(String s) {
if (s.length() < 2) {
return false;
}
final char c = s.charAt(1);
return s.charAt(0) == '0' && (c == 'x' || c == 'X');
}
/**
* The <code>isBin()</code> method checks whether the specifed string represents a binary
* integer. This method only checks the first two characters. If they match "0b" or "0B", then
* this method returns true, otherwise, it returns false.
*
* @param s the string to check whether it begins with a hexadecimal sequence
* @return true if the string begins with "0b" or "0B"; false otherwise
*/
public static boolean isBin(String s) {
if (s.length() < 2) {
return false;
}
final char c = s.charAt(1);
return s.charAt(0) == '0' && (c == 'b' || c == 'B');
}
/**
* The <code>isHexDigit()</code> method tests whether the given character corresponds to one of the
* characters used in the hexadecimal representation (i.e. is '0'-'9' or 'a'-'b', case insensitive. This
* method is generally used in parsing and lexing of input.
*
* @param c the character to test
* @return true if this character is a hexadecimal digit; false otherwise
*/
public static boolean isHexDigit(char c) {
return CharUtil.isHexDigit(c);
}
public static int hexValueOf(char c) {
return CharUtil.hexValueOf(c);
}
public static int octalValueOf(char c) {
return CharUtil.octValueOf(c);
}
public static boolean isOctalDigit(char c) {
return CharUtil.isOctDigit(c);
}
/**
* The <code>justify()</code> method justifies a string to either the right or left margin
* by inserting spaces to pad the string to a specific width. This is useful in printing out
* values in a columnar (aligned) format. This version of the method accepts a string buffer
* into which to put the string.
* @param right a parameter determining whether to justify to the right margin. If this parameter
* is true, the padding spaces will be inserted on the left, before the string.
* @param buf the string buffer into which to write the padded string
* @param s the string to justify
* @param width the width (in characters) to which to justify the string
*/
public static void justify(boolean right, StringBuffer buf, String s, int width) {
final int pad = width - s.length();
if (right) {
space(buf, pad);
buf.append(s);
} else {
buf.append(s);
space(buf, pad);
}
}
public static void justify(boolean right, StringBuffer buf, long l, int width) {
justify(right, buf, Long.toString(l), width);
}
public static void justify(boolean right, StringBuffer buf, float f, int width) {
justify(right, buf, Float.toString(f), width);
}
/**
* The <code>justify()</code> method justifies a string to either the right or left margin
* by inserting spaces to pad the string to a specific width. This is useful in printing out
* values in a columnar (aligned) format.
* @param right a parameter determining whether to justify to the right margin. If this parameter
* is true, the padding spaces will be inserted on the left, before the string.
* @param s the string to justify
* @param width the width (in characters) to which to justify the string
* @return a new string with padding inserted
*/
public static String justify(boolean right, String s, int width) {
// if the string is too wide, return the original
if (width - s.length() <= 0) {
return s;
}
// otherwise, adjust with padding
final StringBuffer buf = new StringBuffer(width);
justify(right, buf, s, width);
return buf.toString();
}
public static String justify(boolean right, long l, int width) {
return justify(right, Long.toString(l), width);
}
public static String justify(boolean right, float f, int width) {
return justify(right, Float.toString(f), width);
}
/**
* The <code>leftJustify()</code> method pads a string to a specified length by adding spaces on the
* right, thus justifying the string to the left margin. This is extremely useful in generating columnar
* output in textual tables.
*
* @param v a long value to convert to a string and justify
* @param width the number of characters to pad the string to
* @return a string representation of the input, padded on the right with spaces to achieve the desired
* length.
*/
public static String leftJustify(long v, int width) {
return justify(false, v, width);
}
/**
* The <code>leftJustify()</code> method pads a string to a specified length by adding spaces on the
* right, thus justifying the string to the left margin. This is extremely useful in generating columnar
* output in textual tables.
*
* @param v a floating point value to convert to a string and justify
* @param width the number of characters to pad the string to
* @return a string representation of the input, padded on the right with spaces to achieve the desired
* length.
*/
public static String leftJustify(float v, int width) {
return justify(false, v, width);
}
/**
* The <code>leftJustify()</code> method pads a string to a specified length by adding spaces on the
* right, thus justifying the string to the left margin. This is extremely useful in generating columnar
* output in textual tables.
*
* @param s a string to justify
* @param width the number of characters to pad the string to
* @return a string representation of the input, padded on the right with spaces to achieve the desired
* length.
*/
public static String leftJustify(String s, int width) {
return justify(false, s, width);
}
/**
* The <code>rightJustify()</code> method pads a string to a specified length by adding spaces on the
* left, thus justifying the string to the right margin. This is extremely useful in generating columnar
* output in textual tables.
*
* @param v a long value to convert to a string and justify
* @param width the number of characters to pad the string to
* @return a string representation of the input, padded on the left with spaces to achieve the desired
* length.
*/
public static String rightJustify(long v, int width) {
return justify(true, v, width);
}
/**
* The <code>rightJustify()</code> method pads a string to a specified length by adding spaces on the
* left, thus justifying the string to the right margin. This is extremely useful in generating columnar
* output in textual tables.
*
* @param v a floating point value to convert to a string and justify
* @param width the number of characters to pad the string to
* @return a string representation of the input, padded on the left with spaces to achieve the desired
* length.
*/
public static String rightJustify(float v, int width) {
return justify(true, v, width);
}
/**
* The <code>rightJustify()</code> method pads a string to a specified length by adding spaces on the
* left, thus justifying the string to the right margin. This is extremely useful in generating columnar
* output in textual tables.
*
* @param s a string to justify
* @param width the number of characters to pad the string to
* @return a string representation of the input, padded on the left with spaces to achieve the desired
* length.
*/
public static String rightJustify(String s, int width) {
return justify(true, s, width);
}
/**
* The <code>toHex()</code> converts the specified long value into a hexadecimal string of the given with.
* The value will be padded on the left with zero values to achieve the desired with.
*
* @param value the long value to convert to a string
* @param width the desired length of the string
* @return a hexadecimal string representation of the given value, padded on the left with zeroes to the
* length specified
*/
public static String toHex(long value, int width) {
return convertToHex(value, width, 0, new char[width], CharUtil.HEX_CHARS);
}
public static String toLowHex(long value, int width) {
return convertToHex(value, width, 0, new char[width], CharUtil.LOW_HEX_CHARS);
}
private static String convertToHex(long value, int width, int start, char[] result, char[] hexChars) {
if (width < 16 && value > (long) 1 << width * 4) {
final StringBuffer buf = new StringBuffer();
for (int cntr = 0; cntr < start; cntr++) {
buf.append(result[cntr]);
}
buf.append(Long.toHexString(value).toUpperCase());
return buf.toString();
}
final int i = start + width - 1;
for (int cntr = 0; cntr < width; cntr++) {
result[i - cntr] = hexChars[(int) (value >> (cntr * 4)) & 0xf];
}
return new String(result);
}
public static String to0xHex(long value, int width) {
final char[] result = new char[width + 2];
result[0] = '0';
result[1] = 'x';
return convertToHex(value, width, 2, result, CharUtil.HEX_CHARS);
}
public static String toBin(long value, int width) {
final char[] result = new char[width];
for (int cntr = 0; cntr < width; cntr++) {
result[width - cntr - 1] = (value & (0x1 << cntr)) == 0 ? '0' : '1';
}
return new String(result);
}
public static void toHex(StringBuffer buf, long value, int width) {
if (value > (long) 1 << width * 4) {
buf.append(Long.toHexString(value).toUpperCase());
return;
}
for (int cntr = width - 1; cntr >= 0; cntr--) {
buf.append(CharUtil.HEX_CHARS[(int) (value >> (cntr * 4)) & 0xf]);
}
}
public static int evaluateIntegerLiteral(String val) {
return readIntegerValue(new StringCharacterIterator(val));
}
public static String formatParagraphs(String s, int leftJust, int pindent, int width) {
final int len = s.length();
int indent = pindent;
indent += leftJust;
int consumed = indent + leftJust;
final String indstr = space(indent);
final String ljstr = space(leftJust);
final StringBuffer buf = new StringBuffer(s.length() + 50);
buf.append(indstr);
int lastSp = -1;
for (int cntr = 0; cntr < len; cntr++) {
final char c = s.charAt(cntr);
if (c == '\n') {
buf.append('\n');
consumed = indent;
buf.append(indstr);
continue;
} else if (Character.isWhitespace(c)) {
lastSp = buf.length();
}
buf.append(c);
consumed++;
if (consumed > width) {
if (lastSp >= 0) {
buf.setCharAt(lastSp, '\n');
buf.insert(lastSp + 1, ljstr);
consumed = buf.length() - lastSp + leftJust - 1;
}
}
}
return buf.toString();
}
/**
* The <code>dup()</code> method takes a character and a count and returns a string where that character
* has been duplicated the specified number of times.
*
* @param c the character to duplicate
* @param len the number of times to duplicate the character
* @return a string representation of the particular character duplicated the specified number of times
*/
public static String dup(char c, int len) {
final StringBuffer buf = new StringBuffer(len);
for (int cntr = 0; cntr < len; cntr++) {
buf.append(c);
}
return buf.toString();
}
protected static final String[] spacers = {
"", // 0
" ", // 1
" ", // 2
" ", // 3
" ", // 4
" ", // 5
" ", // 6
" ", // 7
" ", // 8
" ", // 9
" ", // 10
};
public static String space(int len) {
if (len <= 0) {
return "";
}
if (len < spacers.length) {
return spacers[len];
}
return dup(' ', len);
}
public static void space(StringBuffer buf, int len) {
int i = 0;
while (i++ < len) {
buf.append(' ');
}
}
public static String toDecimal(long value, int places) {
int fract = places;
long val = value;
final StringBuffer buf = new StringBuffer(10 + fract);
while (fract > 0) {
buf.append(val % 10);
fract--;
val = val / 10;
if (fract == 0) {
buf.append('.');
}
}
buf.reverse();
return val + buf.toString();
}
public static char toBit(boolean f) {
return f ? '1' : '0';
}
public static char[] getStringChars(String str) {
final char[] val = new char[str.length()];
str.getChars(0, val.length, val, 0);
return val;
}
}