1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.util; 18 19 import com.google.common.math.LongMath; 20 21 import java.util.regex.Matcher; 22 import java.util.regex.Pattern; 23 24 /** 25 * This is a sentinel type which wraps a {@code Long}. It exists solely as a hint to the 26 * options parsing machinery that a particular value should be parsed as if it were a string 27 * representing a time value. 28 */ 29 @SuppressWarnings("serial") 30 public class TimeVal extends Number implements Comparable<Long> { 31 private static final Pattern TIME_PATTERN = 32 Pattern.compile("(?i)" + // case insensitive 33 "(?:(?<d>\\d+)d)?" + // a number followed by "d" 34 "(?:(?<h>\\d+)h)?" + 35 "(?:(?<m>\\d+)m)?" + 36 "(?:(?<s>\\d+)s)?" + 37 "(?:(?<ms>\\d+)(?:ms)?)?"); // a number followed by "ms" 38 39 private Long mValue = null; 40 41 /** 42 * Constructs a newly allocated TimeVal object that represents the specified Long argument 43 */ TimeVal(Long value)44 public TimeVal(Long value) { 45 mValue = value; 46 } 47 48 /** 49 * Constructs a newly allocated TimeVal object that represents the <emph>timestamp</emph> 50 * indicated by the String parameter. The string is converted to a TimeVal in exactly the 51 * manner used by the {@link #fromString(String)} method. 52 */ TimeVal(String value)53 public TimeVal(String value) throws NumberFormatException { 54 mValue = fromString(value); 55 } 56 57 /** 58 * @return the wrapped {@code Long} value. 59 */ asLong()60 public Long asLong() { 61 return mValue; 62 } 63 64 /** 65 * Parses the string as a hierarchical time value 66 * <p /> 67 * The default unit is millis. The parser will accept {@code s} for seconds (1000 millis), 68 * {@code m} for minutes (60 seconds), {@code h} for hours (60 minutes), or {@code d} for days 69 * (24 hours). 70 * <p /> 71 * Units may be mixed and matched, so long as each unit appears at most once, and so long as 72 * all units which do appear are listed in decreasing order of scale. So, for instance, 73 * {@code h} may only appear before {@code m}, and may only appear after {@code d}. As a 74 * specific example, "1d2h3m4s5ms" would be a valid time value, as would "4" or "4ms". All 75 * embedded whitespace is discarded. 76 * <p /> 77 * Do note that this method rejects overflows. So the output number is guaranteed to be 78 * non-negative, and to fit within the {@code long} type. 79 */ fromString(String value)80 public static long fromString(String value) throws NumberFormatException { 81 if (value == null) throw new NumberFormatException("value is null"); 82 83 try { 84 value = value.replaceAll("\\s+", ""); 85 Matcher m = TIME_PATTERN.matcher(value); 86 if (m.matches()) { 87 // This works by, essentially, modifying the units of timeValue, from the 88 // largest supported unit, until we've dropped down to millis. 89 long timeValue = 0; 90 timeValue = val(m.group("d")); 91 92 // 1 day == 24 hours 93 timeValue = LongMath.checkedMultiply(timeValue, 24); 94 timeValue = LongMath.checkedAdd(timeValue, val(m.group("h"))); 95 96 // 1 hour == 60 minutes 97 timeValue = LongMath.checkedMultiply(timeValue, 60); 98 timeValue = LongMath.checkedAdd(timeValue, val(m.group("m"))); 99 100 // 1 hour == 60 seconds 101 timeValue = LongMath.checkedMultiply(timeValue, 60); 102 timeValue = LongMath.checkedAdd(timeValue, val(m.group("s"))); 103 104 // 1 second == 1000 millis 105 timeValue = LongMath.checkedMultiply(timeValue, 1000); 106 timeValue = LongMath.checkedAdd(timeValue, val(m.group("ms"))); 107 108 return timeValue; 109 } 110 } catch (ArithmeticException e) { 111 throw new NumberFormatException(String.format( 112 "Failed to parse value %s as a time value: %s", value, e.getMessage())); 113 } 114 115 throw new NumberFormatException( 116 String.format("Failed to parse value %s as a time value", value)); 117 } 118 val(String str)119 static long val(String str) throws NumberFormatException { 120 if (str == null) return 0; 121 122 Long value = Long.parseLong(str); 123 return value; 124 } 125 126 127 // implementing interfaces 128 /** 129 * {@inheritDoc} 130 */ 131 @Override doubleValue()132 public double doubleValue() { 133 return mValue.doubleValue(); 134 } 135 136 /** 137 * {@inheritDoc} 138 */ 139 @Override floatValue()140 public float floatValue() { 141 return mValue.floatValue(); 142 } 143 144 /** 145 * {@inheritDoc} 146 */ 147 @Override intValue()148 public int intValue() { 149 return mValue.intValue(); 150 } 151 152 /** 153 * {@inheritDoc} 154 */ 155 @Override longValue()156 public long longValue() { 157 return mValue.longValue(); 158 } 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override compareTo(Long other)164 public int compareTo(Long other) { 165 return mValue.compareTo(other); 166 } 167 } 168