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 045 public enum Unit { 046 TIME_INTERVAL, 047 LONG, 048 BOOLEAN, 049 NONE 050 } 051 052 public static String format(final String value, final Unit unit) { 053 StringBuilder human = new StringBuilder(); 054 switch (unit) { 055 case TIME_INTERVAL: 056 human.append(humanReadableTTL(Long.parseLong(value))); 057 break; 058 case LONG: 059 byte[] longBytes = Bytes.toBytesBinary(value); 060 human.append(String.valueOf(Bytes.toLong(longBytes))); 061 break; 062 case BOOLEAN: 063 byte[] booleanBytes = Bytes.toBytesBinary(value); 064 human.append(String.valueOf(Bytes.toBoolean(booleanBytes))); 065 break; 066 default: 067 human.append(value); 068 } 069 return human.toString(); 070 } 071 072 /** 073 * Convert a human readable string to its value. 074 * @see org.apache.hadoop.hbase.util.PrettyPrinter#format(String, Unit) 075 * @param pretty 076 * @param unit 077 * @return the value corresponding to the human readable string 078 */ 079 public static String valueOf(final String pretty, final Unit unit) throws HBaseException { 080 StringBuilder value = new StringBuilder(); 081 switch (unit) { 082 case TIME_INTERVAL: 083 value.append(humanReadableIntervalToSec(pretty)); 084 break; 085 default: 086 value.append(pretty); 087 } 088 return value.toString(); 089 } 090 091 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="ICAST_INTEGER_MULTIPLY_CAST_TO_LONG", 092 justification="Will not overflow") 093 private static String humanReadableTTL(final long interval){ 094 StringBuilder sb = new StringBuilder(); 095 int days, hours, minutes, seconds; 096 097 // edge cases first 098 if (interval == Integer.MAX_VALUE) { 099 sb.append("FOREVER"); 100 return sb.toString(); 101 } 102 if (interval < HConstants.MINUTE_IN_SECONDS) { 103 sb.append(interval); 104 sb.append(" SECOND").append(interval == 1 ? "" : "S"); 105 return sb.toString(); 106 } 107 108 days = (int) (interval / HConstants.DAY_IN_SECONDS); 109 hours = (int) (interval - HConstants.DAY_IN_SECONDS * days) / HConstants.HOUR_IN_SECONDS; 110 minutes = (int) (interval - HConstants.DAY_IN_SECONDS * days 111 - HConstants.HOUR_IN_SECONDS * hours) / HConstants.MINUTE_IN_SECONDS; 112 seconds = (int) (interval - HConstants.DAY_IN_SECONDS * days 113 - HConstants.HOUR_IN_SECONDS * hours - HConstants.MINUTE_IN_SECONDS * minutes); 114 115 sb.append(interval); 116 sb.append(" SECONDS ("); 117 118 if (days > 0) { 119 sb.append(days); 120 sb.append(" DAY").append(days == 1 ? "" : "S"); 121 } 122 123 if (hours > 0) { 124 sb.append(days > 0 ? " " : ""); 125 sb.append(hours); 126 sb.append(" HOUR").append(hours == 1 ? "" : "S"); 127 } 128 129 if (minutes > 0) { 130 sb.append(days + hours > 0 ? " " : ""); 131 sb.append(minutes); 132 sb.append(" MINUTE").append(minutes == 1 ? "" : "S"); 133 } 134 135 if (seconds > 0) { 136 sb.append(days + hours + minutes > 0 ? " " : ""); 137 sb.append(seconds); 138 sb.append(" SECOND").append(minutes == 1 ? "" : "S"); 139 } 140 141 sb.append(")"); 142 143 return sb.toString(); 144 } 145 146 /** 147 * Convert a human readable time interval to seconds. Examples of the human readable 148 * time intervals are: 50 DAYS 1 HOUR 30 MINUTES , 25000 SECONDS etc. 149 * The units of time specified can be in uppercase as well as lowercase. Also, if a 150 * single number is specified without any time unit, it is assumed to be in seconds. 151 * @param humanReadableInterval 152 * @return value in seconds 153 */ 154 private static long humanReadableIntervalToSec(final String humanReadableInterval) 155 throws HBaseException { 156 if (humanReadableInterval == null || humanReadableInterval.equalsIgnoreCase("FOREVER")) { 157 return HConstants.FOREVER; 158 } 159 160 try { 161 return Long.parseLong(humanReadableInterval); 162 } catch(NumberFormatException ex) { 163 LOG.debug("Given interval value is not a number, parsing for human readable format"); 164 } 165 166 String days = null; 167 String hours = null; 168 String minutes = null; 169 String seconds = null; 170 String expectedTtl = null; 171 long ttl; 172 173 Matcher matcher = PrettyPrinter.INTERVAL_PATTERN.matcher(humanReadableInterval); 174 if (matcher.matches()) { 175 expectedTtl = matcher.group(2); 176 days = matcher.group(4); 177 hours = matcher.group(6); 178 minutes = matcher.group(8); 179 seconds = matcher.group(10); 180 } 181 ttl = 0; 182 ttl += days != null ? Long.parseLong(days)*HConstants.DAY_IN_SECONDS:0; 183 ttl += hours != null ? Long.parseLong(hours)*HConstants.HOUR_IN_SECONDS:0; 184 ttl += minutes != null ? Long.parseLong(minutes)*HConstants.MINUTE_IN_SECONDS:0; 185 ttl += seconds != null ? Long.parseLong(seconds):0; 186 187 if (expectedTtl != null && Long.parseLong(expectedTtl) != ttl) { 188 throw new HBaseException("Malformed TTL string: TTL values in seconds and human readable" + 189 "format do not match"); 190 } 191 return ttl; 192 } 193 194 /** 195 * Pretty prints a collection of any type to a string. Relies on toString() implementation of the 196 * object type. 197 * @param collection collection to pretty print. 198 * @return Pretty printed string for the collection. 199 */ 200 public static String toString(Collection<?> collection) { 201 List<String> stringList = new ArrayList<>(); 202 for (Object o: collection) { 203 stringList.add(Objects.toString(o)); 204 } 205 return "[" + String.join(",", stringList) + "]"; 206 } 207 208}