blob: 57a082d290c53f626840ab9cb973d8a1809607fa [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 org.apache.impala.common;
import static org.apache.impala.common.ByteUnits.GIGABYTE;
import static org.apache.impala.common.ByteUnits.KILOBYTE;
import static org.apache.impala.common.ByteUnits.MEGABYTE;
import static org.apache.impala.common.ByteUnits.PETABYTE;
import static org.apache.impala.common.ByteUnits.TERABYTE;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.text.WordUtils;
import com.google.common.base.Joiner;
/**
* Utility functions for pretty printing.
*/
public class PrintUtils {
/**
* Prints the given number of bytes in PB, TB, GB, MB, KB with 2 decimal points.
* For example 5000 will be returned as 4.88KB.
*/
public static String printBytes(long bytes) {
double result = bytes;
// Avoid String.format() due to IMPALA-1572 which happens on JDK7 but not JDK6.
// IMPALA-6759: Please update tests/stress/concurrent_select.py MEM_ESTIMATE_PATTERN
// if you add additional unit prefixes.
if (bytes >= PETABYTE) return new DecimalFormat(".00PB").format(result / PETABYTE);
if (bytes >= TERABYTE) return new DecimalFormat(".00TB").format(result / TERABYTE);
if (bytes >= GIGABYTE) return new DecimalFormat(".00GB").format(result / GIGABYTE);
if (bytes >= MEGABYTE) return new DecimalFormat(".00MB").format(result / MEGABYTE);
if (bytes >= KILOBYTE) return new DecimalFormat(".00KB").format(result / KILOBYTE);
return bytes + "B";
}
public static final long KILO = 1000;
public static final long MEGA = KILO * 1000;
public static final long GIGA = MEGA * 1000;
public static final long TERA = GIGA * 1000;
/**
* Print a value using simple metric (power of 1000) units. Units are
* (none), K, M, G or T. Value has two digits past the decimal point.
*/
public static String printMetric(long value) {
double result = value;
if (value >= TERA) return new DecimalFormat(".00T").format(result / TERA);
if (value >= GIGA) return new DecimalFormat(".00G").format(result / GIGA);
if (value >= MEGA) return new DecimalFormat(".00M").format(result / MEGA);
if (value >= KILO) return new DecimalFormat(".00K").format(result / KILO);
return Long.toString(value);
}
/**
* Pattern to use when searching for a metric-encoded value.
*/
public static final String METRIC_REGEX = "(\\d+(?:.\\d+)?)([TGMK]?)";
/**
* Pattern to use when searching for or parsing a metric-encoded value.
*/
public static final Pattern METRIC_PATTERN =
Pattern.compile(METRIC_REGEX, Pattern.CASE_INSENSITIVE);
/**
* Decode a value metric-encoded using {@link #printMetric(long)}.
* @param value metric-encoded string
* @return approximate numeric value, or -1 if the value is invalid
* (metric encoded strings can never be negative normally)
*/
public static double decodeMetric(String value) {
Matcher m = METRIC_PATTERN.matcher(value);
if (! m.matches()) return -1;
return decodeMetric(m.group(1), m.group(2));
}
/**
* Decode a metric-encoded string already parsed into parts.
* @param valueStr numeric part of the value
* @param units units part of the value
* @return approximate numeric value
*/
// Yes, "PrintUtils" is an odd place for a parse function, but
// best to keep the formatter and parser together.
public static double decodeMetric(String valueStr, String units) {
double value = Double.parseDouble(valueStr);
switch (units.toUpperCase()) {
case "":
return value;
case "K":
return value * KILO;
case "M":
return value * MEGA;
case "G":
return value * GIGA;
case "T":
return value * TERA;
default:
return -1;
}
}
/**
* Same as printBytes() except 0 decimal points are shown for MB and KB.
*/
public static String printBytesRoundedToMb(long bytes) {
double result = bytes;
// Avoid String.format() due to IMPALA-1572 which happens on JDK7 but not JDK6.
// IMPALA-6759: Please update tests/stress/concurrent_select.py MEM_ESTIMATE_PATTERN
// if you add additional unit prefixes.
if (bytes >= PETABYTE) return new DecimalFormat(".00PB").format(result / PETABYTE);
if (bytes >= TERABYTE) return new DecimalFormat(".00TB").format(result / TERABYTE);
if (bytes >= GIGABYTE) return new DecimalFormat(".00GB").format(result / GIGABYTE);
if (bytes >= MEGABYTE) return new DecimalFormat("0MB").format(result / MEGABYTE);
if (bytes >= KILOBYTE) return new DecimalFormat("0KB").format(result / KILOBYTE);
return bytes + "B";
}
/**
* Print an estimated cardinality. No need to print the exact value
* because estimates are not super-precise.
*/
public static String printEstCardinality(long cardinality) {
return (cardinality != -1) ? printMetric(cardinality) : "unavailable";
}
/**
* Print an exact cardinality (such as a row count) as one of three formats:
*
* * "unavailable" (if value < 0)
* * number (if value is small)
* * xx.xxU (dd,ddd) (if the value is large)
*/
public static String printExactCardinality(long value) {
if (value == -1) return "unavailable";
String result = printMetric(value);
if (value < KILO) return result;
return String.format("%s (%,d)", result, value);
}
public static String printNumHosts(String prefix, long numHosts) {
return prefix + "hosts=" + ((numHosts != -1) ? numHosts : "unavailable");
}
public static String printNumInstances(String prefix, long numInstances) {
return prefix + "instances=" + ((numInstances != -1) ? numInstances : "unavailable");
}
/**
* Prints the given square matrix into matrixStr. Separates cells by cellSpacing.
*/
public static void printMatrix(
boolean[][] matrix, int cellSpacing, StringBuilder matrixStr) {
// Print labels.
matrixStr.append(StringUtils.repeat(' ', cellSpacing));
String formatStr = "%Xd".replace("X", String.valueOf(cellSpacing));
for (int i = 0; i < matrix.length; ++i) {
matrixStr.append(String.format(formatStr, i));
}
matrixStr.append("\n");
// Print matrix.
for (int i = 0; i < matrix.length; ++i) {
matrixStr.append(String.format(formatStr, i));
for (int j = 0; j < matrix.length; ++j) {
int cell = (matrix[i][j]) ? 1 : 0;
matrixStr.append(String.format(formatStr, cell));
}
matrixStr.append("\n");
}
}
/**
* Wrap a string by inserting newlines so that no line exceeds a given length.
* Any newlines in the input are maintained.
*/
public static String wrapString(String s, int wrapLength) {
String wrapped = WordUtils.wrap(s, wrapLength, null, true);
// Remove any trailing blanks from a line.
wrapped = wrapped.replaceAll(" +$", "");
return wrapped;
}
/**
* Join the objects in 'objects' in a human-readable form, such as
* "'obj 1', 'obj 2', 'obj 3'". No escaping of quotes is performed.
*/
public static String joinQuoted(Iterable<?> objs) {
Iterator<?> it = objs.iterator();
if (!it.hasNext()) return "";
return "'" + Joiner.on("', '").join(it) + "'";
}
}