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}