View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.util;
21  
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.hbase.HConstants;
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.exceptions.HBaseException;
30  
31  @InterfaceAudience.Private
32  public class PrettyPrinter {
33  
34    private static final Log LOG = LogFactory.getLog(PrettyPrinter.class);
35  
36    private static final String INTERVAL_REGEX = "((\\d+)\\s*SECONDS?\\s*\\()?\\s*" +
37            "((\\d+)\\s*DAYS?)?\\s*((\\d+)\\s*HOURS?)?\\s*" +
38            "((\\d+)\\s*MINUTES?)?\\s*((\\d+)\\s*SECONDS?)?\\s*\\)?";
39    private static final Pattern INTERVAL_PATTERN = Pattern.compile(INTERVAL_REGEX,
40            Pattern.CASE_INSENSITIVE);
41  
42    public enum Unit {
43      TIME_INTERVAL,
44      LONG,
45      BOOLEAN,
46      NONE
47    }
48  
49    public static String format(final String value, final Unit unit) {
50      StringBuilder human = new StringBuilder();
51      switch (unit) {
52        case TIME_INTERVAL:
53          human.append(humanReadableTTL(Long.parseLong(value)));
54          break;
55        case LONG:
56          byte[] longBytes = Bytes.toBytesBinary(value);
57          human.append(String.valueOf(Bytes.toLong(longBytes)));
58          break;
59        case BOOLEAN:
60          byte[] booleanBytes = Bytes.toBytesBinary(value);
61          human.append(String.valueOf(Bytes.toBoolean(booleanBytes)));
62          break;
63        default:
64          human.append(value);
65      }
66      return human.toString();
67    }
68  
69    /**
70     * Convert a human readable string to its value.
71     * @see org.apache.hadoop.hbase.util.PrettyPrinter#format(String, Unit)
72     * @param pretty
73     * @param unit
74     * @return the value corresponding to the human readable string
75     */
76    public static String valueOf(final String pretty, final Unit unit) throws HBaseException {
77      StringBuilder value = new StringBuilder();
78      switch (unit) {
79        case TIME_INTERVAL:
80          value.append(humanReadableIntervalToSec(pretty));
81          break;
82        default:
83          value.append(pretty);
84      }
85      return value.toString();
86    }
87  
88    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="ICAST_INTEGER_MULTIPLY_CAST_TO_LONG",
89        justification="Will not overflow")
90    private static String humanReadableTTL(final long interval){
91      StringBuilder sb = new StringBuilder();
92      int days, hours, minutes, seconds;
93  
94      // edge cases first
95      if (interval == Integer.MAX_VALUE) {
96        sb.append("FOREVER");
97        return sb.toString();
98      }
99      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 }