001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.util; 019 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.List; 023import java.util.Objects; 024import java.util.regex.Matcher; 025import java.util.regex.Pattern; 026import org.apache.hadoop.hbase.HConstants; 027import org.apache.hadoop.hbase.exceptions.HBaseException; 028import org.apache.yetus.audience.InterfaceAudience; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032@InterfaceAudience.Private 033public final class PrettyPrinter { 034 035 private static final Logger LOG = LoggerFactory.getLogger(PrettyPrinter.class); 036 037 private static final String INTERVAL_REGEX = 038 "((\\d+)\\s*SECONDS?\\s*\\()?\\s*" + "((\\d+)\\s*DAYS?)?\\s*((\\d+)\\s*HOURS?)?\\s*" 039 + "((\\d+)\\s*MINUTES?)?\\s*((\\d+)\\s*SECONDS?)?\\s*\\)?"; 040 private static final Pattern INTERVAL_PATTERN = 041 Pattern.compile(INTERVAL_REGEX, Pattern.CASE_INSENSITIVE); 042 private static final String SIZE_REGEX = 043 "((\\d+)\\s*B?\\s*\\()?\\s*" + "((\\d+)\\s*TB?)?\\s*((\\d+)\\s*GB?)?\\s*" 044 + "((\\d+)\\s*MB?)?\\s*((\\d+)\\s*KB?)?\\s*((\\d+)\\s*B?)?\\s*\\)?"; 045 private static final Pattern SIZE_PATTERN = Pattern.compile(SIZE_REGEX, Pattern.CASE_INSENSITIVE); 046 047 public enum Unit { 048 TIME_INTERVAL, 049 LONG, 050 BOOLEAN, 051 BYTE, 052 NONE 053 } 054 055 public static String format(final String value, final Unit unit) { 056 StringBuilder human = new StringBuilder(); 057 switch (unit) { 058 case TIME_INTERVAL: 059 human.append(humanReadableTTL(Long.parseLong(value))); 060 break; 061 case LONG: 062 byte[] longBytes = Bytes.toBytesBinary(value); 063 human.append(String.valueOf(Bytes.toLong(longBytes))); 064 break; 065 case BOOLEAN: 066 byte[] booleanBytes = Bytes.toBytesBinary(value); 067 human.append(String.valueOf(Bytes.toBoolean(booleanBytes))); 068 break; 069 case BYTE: 070 human.append(humanReadableByte(Long.parseLong(value))); 071 break; 072 default: 073 human.append(value); 074 } 075 return human.toString(); 076 } 077 078 /** 079 * Convert a human readable string to its value. 080 * @see org.apache.hadoop.hbase.util.PrettyPrinter#format(String, Unit) 081 * @return the value corresponding to the human readable string 082 */ 083 public static String valueOf(final String pretty, final Unit unit) throws HBaseException { 084 StringBuilder value = new StringBuilder(); 085 switch (unit) { 086 case TIME_INTERVAL: 087 value.append(humanReadableIntervalToSec(pretty)); 088 break; 089 case BYTE: 090 value.append(humanReadableSizeToBytes(pretty)); 091 break; 092 default: 093 value.append(pretty); 094 } 095 return value.toString(); 096 } 097 098 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "ICAST_INTEGER_MULTIPLY_CAST_TO_LONG", 099 justification = "Will not overflow") 100 private static String humanReadableTTL(final long interval) { 101 StringBuilder sb = new StringBuilder(); 102 int days, hours, minutes, seconds; 103 104 // edge cases first 105 if (interval == Integer.MAX_VALUE) { 106 sb.append("FOREVER"); 107 return sb.toString(); 108 } 109 if (interval < HConstants.MINUTE_IN_SECONDS) { 110 sb.append(interval); 111 sb.append(" SECOND").append(interval == 1 ? "" : "S"); 112 return sb.toString(); 113 } 114 115 days = (int) (interval / HConstants.DAY_IN_SECONDS); 116 hours = (int) (interval - HConstants.DAY_IN_SECONDS * days) / HConstants.HOUR_IN_SECONDS; 117 minutes = 118 (int) (interval - HConstants.DAY_IN_SECONDS * days - HConstants.HOUR_IN_SECONDS * hours) 119 / HConstants.MINUTE_IN_SECONDS; 120 seconds = (int) (interval - HConstants.DAY_IN_SECONDS * days 121 - HConstants.HOUR_IN_SECONDS * hours - HConstants.MINUTE_IN_SECONDS * minutes); 122 123 sb.append(interval); 124 sb.append(" SECONDS ("); 125 126 if (days > 0) { 127 sb.append(days); 128 sb.append(" DAY").append(days == 1 ? "" : "S"); 129 } 130 131 if (hours > 0) { 132 sb.append(days > 0 ? " " : ""); 133 sb.append(hours); 134 sb.append(" HOUR").append(hours == 1 ? "" : "S"); 135 } 136 137 if (minutes > 0) { 138 sb.append(days + hours > 0 ? " " : ""); 139 sb.append(minutes); 140 sb.append(" MINUTE").append(minutes == 1 ? "" : "S"); 141 } 142 143 if (seconds > 0) { 144 sb.append(days + hours + minutes > 0 ? " " : ""); 145 sb.append(seconds); 146 sb.append(" SECOND").append(minutes == 1 ? "" : "S"); 147 } 148 149 sb.append(")"); 150 151 return sb.toString(); 152 } 153 154 /** 155 * Convert a human readable time interval to seconds. Examples of the human readable time 156 * intervals are: 50 DAYS 1 HOUR 30 MINUTES , 25000 SECONDS etc. The units of time specified can 157 * be in uppercase as well as lowercase. Also, if a single number is specified without any time 158 * unit, it is assumed to be in seconds. 159 * @return value in seconds 160 */ 161 private static long humanReadableIntervalToSec(final String humanReadableInterval) 162 throws HBaseException { 163 if (humanReadableInterval == null || humanReadableInterval.equalsIgnoreCase("FOREVER")) { 164 return HConstants.FOREVER; 165 } 166 167 try { 168 return Long.parseLong(humanReadableInterval); 169 } catch (NumberFormatException ex) { 170 LOG.debug("Given interval value is not a number, parsing for human readable format"); 171 } 172 173 String days = null; 174 String hours = null; 175 String minutes = null; 176 String seconds = null; 177 String expectedTtl = null; 178 long ttl; 179 180 Matcher matcher = PrettyPrinter.INTERVAL_PATTERN.matcher(humanReadableInterval); 181 if (matcher.matches()) { 182 expectedTtl = matcher.group(2); 183 days = matcher.group(4); 184 hours = matcher.group(6); 185 minutes = matcher.group(8); 186 seconds = matcher.group(10); 187 } else { 188 LOG.warn("Given interval value '{}' is not a number and does not match human readable format," 189 + " value will be set to 0.", humanReadableInterval); 190 } 191 192 ttl = 0; 193 ttl += days != null ? Long.parseLong(days) * HConstants.DAY_IN_SECONDS : 0; 194 ttl += hours != null ? Long.parseLong(hours) * HConstants.HOUR_IN_SECONDS : 0; 195 ttl += minutes != null ? Long.parseLong(minutes) * HConstants.MINUTE_IN_SECONDS : 0; 196 ttl += seconds != null ? Long.parseLong(seconds) : 0; 197 198 if (expectedTtl != null && Long.parseLong(expectedTtl) != ttl) { 199 throw new HBaseException( 200 "Malformed TTL string: TTL values in seconds and human readable" + "format do not match"); 201 } 202 return ttl; 203 } 204 205 /** 206 * Convert a long size to a human readable string. Example: 10763632640 -> 10763632640 B (10GB 207 * 25MB) 208 * @param size the size in bytes 209 * @return human readable string 210 */ 211 private static String humanReadableByte(final long size) { 212 StringBuilder sb = new StringBuilder(); 213 long tb, gb, mb, kb, b; 214 215 if (size < HConstants.KB_IN_BYTES) { 216 sb.append(size); 217 sb.append(" B"); 218 return sb.toString(); 219 } 220 221 tb = size / HConstants.TB_IN_BYTES; 222 gb = (size - HConstants.TB_IN_BYTES * tb) / HConstants.GB_IN_BYTES; 223 mb = 224 (size - HConstants.TB_IN_BYTES * tb - HConstants.GB_IN_BYTES * gb) / HConstants.MB_IN_BYTES; 225 kb = (size - HConstants.TB_IN_BYTES * tb - HConstants.GB_IN_BYTES * gb 226 - HConstants.MB_IN_BYTES * mb) / HConstants.KB_IN_BYTES; 227 b = (size - HConstants.TB_IN_BYTES * tb - HConstants.GB_IN_BYTES * gb 228 - HConstants.MB_IN_BYTES * mb - HConstants.KB_IN_BYTES * kb); 229 230 sb.append(size).append(" B ("); 231 if (tb > 0) { 232 sb.append(tb); 233 sb.append("TB"); 234 } 235 236 if (gb > 0) { 237 sb.append(tb > 0 ? " " : ""); 238 sb.append(gb); 239 sb.append("GB"); 240 } 241 242 if (mb > 0) { 243 sb.append(tb + gb > 0 ? " " : ""); 244 sb.append(mb); 245 sb.append("MB"); 246 } 247 248 if (kb > 0) { 249 sb.append(tb + gb + mb > 0 ? " " : ""); 250 sb.append(kb); 251 sb.append("KB"); 252 } 253 254 if (b > 0) { 255 sb.append(tb + gb + mb + kb > 0 ? " " : ""); 256 sb.append(b); 257 sb.append("B"); 258 } 259 260 sb.append(")"); 261 return sb.toString(); 262 } 263 264 /** 265 * Convert a human readable size to bytes. Examples of the human readable size are: 50 GB 20 MB 1 266 * KB , 25000 B etc. The units of size specified can be in uppercase as well as lowercase. Also, 267 * if a single number is specified without any time unit, it is assumed to be in bytes. 268 * @param humanReadableSize human readable size 269 * @return value in bytes 270 */ 271 private static long humanReadableSizeToBytes(final String humanReadableSize) 272 throws HBaseException { 273 if (humanReadableSize == null) { 274 return -1; 275 } 276 277 try { 278 return Long.parseLong(humanReadableSize); 279 } catch (NumberFormatException ex) { 280 LOG.debug("Given size value is not a number, parsing for human readable format"); 281 } 282 283 String tb = null; 284 String gb = null; 285 String mb = null; 286 String kb = null; 287 String b = null; 288 String expectedSize = null; 289 long size = 0; 290 291 Matcher matcher = PrettyPrinter.SIZE_PATTERN.matcher(humanReadableSize); 292 if (matcher.matches()) { 293 expectedSize = matcher.group(2); 294 tb = matcher.group(4); 295 gb = matcher.group(6); 296 mb = matcher.group(8); 297 kb = matcher.group(10); 298 b = matcher.group(12); 299 } 300 size += tb != null ? Long.parseLong(tb) * HConstants.TB_IN_BYTES : 0; 301 size += gb != null ? Long.parseLong(gb) * HConstants.GB_IN_BYTES : 0; 302 size += mb != null ? Long.parseLong(mb) * HConstants.MB_IN_BYTES : 0; 303 size += kb != null ? Long.parseLong(kb) * HConstants.KB_IN_BYTES : 0; 304 size += b != null ? Long.parseLong(b) : 0; 305 306 if (expectedSize != null && Long.parseLong(expectedSize) != size) { 307 throw new HBaseException( 308 "Malformed size string: values in byte and human readable" + "format do not match"); 309 } 310 return size; 311 } 312 313 /** 314 * Pretty prints a collection of any type to a string. Relies on toString() implementation of the 315 * object type. 316 * @param collection collection to pretty print. 317 * @return Pretty printed string for the collection. 318 */ 319 public static String toString(Collection<?> collection) { 320 List<String> stringList = new ArrayList<>(); 321 for (Object o : collection) { 322 stringList.add(Objects.toString(o)); 323 } 324 return "[" + String.join(",", stringList) + "]"; 325 } 326 327}