| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.sshd.common.config; |
| |
| import java.util.Collections; |
| import java.util.EnumMap; |
| import java.util.EnumSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.sshd.common.util.GenericUtils; |
| import org.apache.sshd.common.util.MapEntryUtils; |
| |
| /** |
| * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a> |
| * @see <A HREF="http://unixhelp.ed.ac.uk/CGI/man-cgi?sshd_config+5">Time formats for SSH configuration values</A> |
| */ |
| public enum TimeValueConfig { |
| SECONDS('s', 'S', TimeUnit.SECONDS.toMillis(1L)), |
| MINUTES('m', 'M', TimeUnit.MINUTES.toMillis(1L)), |
| HOURS('h', 'H', TimeUnit.HOURS.toMillis(1L)), |
| DAYS('d', 'D', TimeUnit.DAYS.toMillis(1L)), |
| WEEKS('w', 'W', TimeUnit.DAYS.toMillis(7L)); |
| |
| public static final Set<TimeValueConfig> VALUES = Collections.unmodifiableSet(EnumSet.allOf(TimeValueConfig.class)); |
| |
| private final char loChar; |
| private final char hiChar; |
| private final long interval; |
| |
| TimeValueConfig(char lo, char hi, long interval) { |
| loChar = lo; |
| hiChar = hi; |
| this.interval = interval; |
| } |
| |
| public final char getLowerCaseValue() { |
| return loChar; |
| } |
| |
| public final char getUpperCaseValue() { |
| return hiChar; |
| } |
| |
| public final long getInterval() { |
| return interval; |
| } |
| |
| public static TimeValueConfig fromValueChar(char ch) { |
| if ((ch <= ' ') || (ch >= 0x7F)) { |
| return null; |
| } |
| |
| for (TimeValueConfig v : VALUES) { |
| if ((v.getLowerCaseValue() == ch) || (v.getUpperCaseValue() == ch)) { |
| return v; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @param s A time specification |
| * @return The specified duration in milliseconds |
| * @see #parse(String) |
| * @see #durationOf(Map) |
| */ |
| public static long durationOf(String s) { |
| Map<TimeValueConfig, Long> spec = parse(s); |
| return durationOf(spec); |
| } |
| |
| /** |
| * @param s An input time specification containing possibly mixed numbers and units - e.g., |
| * {@code 3h10m} to indicate 3 hours and 10 minutes |
| * @return A {@link Map} specifying for each time unit its count |
| * @throws NumberFormatException If bad numbers found - e.g., negative counts |
| * @throws IllegalArgumentException If bad format - e.g., unknown unit |
| */ |
| public static Map<TimeValueConfig, Long> parse(String s) throws IllegalArgumentException { |
| if (GenericUtils.isEmpty(s)) { |
| return Collections.emptyMap(); |
| } |
| |
| int lastPos = 0; |
| Map<TimeValueConfig, Long> spec = new EnumMap<>(TimeValueConfig.class); |
| for (int curPos = 0; curPos < s.length(); curPos++) { |
| char ch = s.charAt(curPos); |
| if ((ch >= '0') && (ch <= '9')) { |
| continue; |
| } |
| |
| if (curPos <= lastPos) { |
| throw new IllegalArgumentException("parse(" + s + ") missing count value at index=" + curPos); |
| } |
| |
| TimeValueConfig c = fromValueChar(ch); |
| if (c == null) { |
| throw new IllegalArgumentException("parse(" + s + ") unknown time value character: '" + ch + "'"); |
| } |
| |
| String v = s.substring(lastPos, curPos); |
| long count = Long.parseLong(v); |
| if (count < 0L) { |
| throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for " + c.name()); |
| } |
| |
| Long prev = spec.put(c, count); |
| if (prev != null) { |
| throw new IllegalArgumentException( |
| "parse(" + s + ") " + c.name() + " value re-specified: current=" + count + ", previous=" + prev); |
| } |
| |
| lastPos = curPos + 1; |
| if (lastPos >= s.length()) { |
| break; |
| } |
| } |
| |
| if (lastPos < s.length()) { |
| String v = s.substring(lastPos); |
| long count = Long.parseLong(v); |
| if (count < 0L) { |
| throw new IllegalArgumentException("parse(" + s + ") negative count (" + v + ") for last component"); |
| } |
| |
| Long prev = spec.put(SECONDS, count); |
| if (prev != null) { |
| throw new IllegalArgumentException( |
| "parse(" + s + ") last component (" + SECONDS.name() + ") value re-specified: current=" + count |
| + ", previous=" + prev); |
| } |
| } |
| |
| return spec; |
| } |
| |
| /** |
| * @param spec The {@link Map} specifying the count for each {@link TimeValueConfig} |
| * @return The total duration in milliseconds |
| * @throws IllegalArgumentException If negative count for a time unit |
| */ |
| public static long durationOf(Map<TimeValueConfig, ? extends Number> spec) throws IllegalArgumentException { |
| if (MapEntryUtils.isEmpty(spec)) { |
| return -1L; |
| } |
| |
| long total = 0L; |
| // Cannot use forEach because the total value is not effectively final |
| for (Map.Entry<TimeValueConfig, ? extends Number> se : spec.entrySet()) { |
| TimeValueConfig v = se.getKey(); |
| Number c = se.getValue(); |
| long factor = c.longValue(); |
| if (factor < 0L) { |
| throw new IllegalArgumentException("valueOf(" + spec + ") bad factor (" + c + ") for " + v.name()); |
| } |
| |
| long added = v.getInterval() * factor; |
| total += added; |
| } |
| |
| return total; |
| } |
| } |