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