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