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) nn * @return the value 081 * 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. n * @return value in seconds 159 */ 160 private static long humanReadableIntervalToSec(final String humanReadableInterval) 161 throws HBaseException { 162 if (humanReadableInterval == null || humanReadableInterval.equalsIgnoreCase("FOREVER")) { 163 return HConstants.FOREVER; 164 } 165 166 try { 167 return Long.parseLong(humanReadableInterval); 168 } catch (NumberFormatException ex) { 169 LOG.debug("Given interval value is not a number, parsing for human readable format"); 170 } 171 172 String days = null; 173 String hours = null; 174 String minutes = null; 175 String seconds = null; 176 String expectedTtl = null; 177 long ttl; 178 179 Matcher matcher = PrettyPrinter.INTERVAL_PATTERN.matcher(humanReadableInterval); 180 if (matcher.matches()) { 181 expectedTtl = matcher.group(2); 182 days = matcher.group(4); 183 hours = matcher.group(6); 184 minutes = matcher.group(8); 185 seconds = matcher.group(10); 186 } 187 ttl = 0; 188 ttl += days != null ? Long.parseLong(days) * HConstants.DAY_IN_SECONDS : 0; 189 ttl += hours != null ? Long.parseLong(hours) * HConstants.HOUR_IN_SECONDS : 0; 190 ttl += minutes != null ? Long.parseLong(minutes) * HConstants.MINUTE_IN_SECONDS : 0; 191 ttl += seconds != null ? Long.parseLong(seconds) : 0; 192 193 if (expectedTtl != null && Long.parseLong(expectedTtl) != ttl) { 194 throw new HBaseException( 195 "Malformed TTL string: TTL values in seconds and human readable" + "format do not match"); 196 } 197 return ttl; 198 } 199 200 /** 201 * Convert a long size to a human readable string. Example: 10763632640 -> 10763632640 B (10GB 202 * 25MB) 203 * @param size the size in bytes 204 * @return human readable string 205 */ 206 private static String humanReadableByte(final long size) { 207 StringBuilder sb = new StringBuilder(); 208 long tb, gb, mb, kb, b; 209 210 if (size < HConstants.KB_IN_BYTES) { 211 sb.append(size); 212 sb.append(" B"); 213 return sb.toString(); 214 } 215 216 tb = size / HConstants.TB_IN_BYTES; 217 gb = (size - HConstants.TB_IN_BYTES * tb) / HConstants.GB_IN_BYTES; 218 mb = 219 (size - HConstants.TB_IN_BYTES * tb - HConstants.GB_IN_BYTES * gb) / HConstants.MB_IN_BYTES; 220 kb = (size - HConstants.TB_IN_BYTES * tb - HConstants.GB_IN_BYTES * gb 221 - HConstants.MB_IN_BYTES * mb) / HConstants.KB_IN_BYTES; 222 b = (size - HConstants.TB_IN_BYTES * tb - HConstants.GB_IN_BYTES * gb 223 - HConstants.MB_IN_BYTES * mb - HConstants.KB_IN_BYTES * kb); 224 225 sb.append(size).append(" B ("); 226 if (tb > 0) { 227 sb.append(tb); 228 sb.append("TB"); 229 } 230 231 if (gb > 0) { 232 sb.append(tb > 0 ? " " : ""); 233 sb.append(gb); 234 sb.append("GB"); 235 } 236 237 if (mb > 0) { 238 sb.append(tb + gb > 0 ? " " : ""); 239 sb.append(mb); 240 sb.append("MB"); 241 } 242 243 if (kb > 0) { 244 sb.append(tb + gb + mb > 0 ? " " : ""); 245 sb.append(kb); 246 sb.append("KB"); 247 } 248 249 if (b > 0) { 250 sb.append(tb + gb + mb + kb > 0 ? " " : ""); 251 sb.append(b); 252 sb.append("B"); 253 } 254 255 sb.append(")"); 256 return sb.toString(); 257 } 258 259 /** 260 * Convert a human readable size to bytes. Examples of the human readable size are: 50 GB 20 MB 1 261 * KB , 25000 B etc. The units of size specified can be in uppercase as well as lowercase. Also, 262 * if a single number is specified without any time unit, it is assumed to be in bytes. 263 * @param humanReadableSize human readable size 264 * @return value in bytes n 265 */ 266 private static long humanReadableSizeToBytes(final String humanReadableSize) 267 throws HBaseException { 268 if (humanReadableSize == null) { 269 return -1; 270 } 271 272 try { 273 return Long.parseLong(humanReadableSize); 274 } catch (NumberFormatException ex) { 275 LOG.debug("Given size value is not a number, parsing for human readable format"); 276 } 277 278 String tb = null; 279 String gb = null; 280 String mb = null; 281 String kb = null; 282 String b = null; 283 String expectedSize = null; 284 long size = 0; 285 286 Matcher matcher = PrettyPrinter.SIZE_PATTERN.matcher(humanReadableSize); 287 if (matcher.matches()) { 288 expectedSize = matcher.group(2); 289 tb = matcher.group(4); 290 gb = matcher.group(6); 291 mb = matcher.group(8); 292 kb = matcher.group(10); 293 b = matcher.group(12); 294 } 295 size += tb != null ? Long.parseLong(tb) * HConstants.TB_IN_BYTES : 0; 296 size += gb != null ? Long.parseLong(gb) * HConstants.GB_IN_BYTES : 0; 297 size += mb != null ? Long.parseLong(mb) * HConstants.MB_IN_BYTES : 0; 298 size += kb != null ? Long.parseLong(kb) * HConstants.KB_IN_BYTES : 0; 299 size += b != null ? Long.parseLong(b) : 0; 300 301 if (expectedSize != null && Long.parseLong(expectedSize) != size) { 302 throw new HBaseException( 303 "Malformed size string: values in byte and human readable" + "format do not match"); 304 } 305 return size; 306 } 307 308 /** 309 * Pretty prints a collection of any type to a string. Relies on toString() implementation of the 310 * object type. 311 * @param collection collection to pretty print. 312 * @return Pretty printed string for the collection. 313 */ 314 public static String toString(Collection<?> collection) { 315 List<String> stringList = new ArrayList<>(); 316 for (Object o : collection) { 317 stringList.add(Objects.toString(o)); 318 } 319 return "[" + String.join(",", stringList) + "]"; 320 } 321 322}