http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/brooklyn/util/time/Duration.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/brooklyn/util/time/Duration.java b/utils/common/src/main/java/brooklyn/util/time/Duration.java deleted file mode 100644 index 3107bf4..0000000 --- a/utils/common/src/main/java/brooklyn/util/time/Duration.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * 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 brooklyn.util.time; - -import static com.google.common.base.Preconditions.checkNotNull; - -import java.io.Serializable; -import java.lang.reflect.Method; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nullable; - -import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.base.Stopwatch; - -/** simple class determines a length of time */ -public class Duration implements Comparable<Duration>, Serializable { - - private static final long serialVersionUID = -2303909964519279617L; - - public static final Duration ZERO = of(0, null); - public static final Duration ONE_MILLISECOND = of(1, TimeUnit.MILLISECONDS); - public static final Duration ONE_SECOND = of(1, TimeUnit.SECONDS); - public static final Duration FIVE_SECONDS = of(5, TimeUnit.SECONDS); - public static final Duration TEN_SECONDS = of(10, TimeUnit.SECONDS); - public static final Duration THIRTY_SECONDS = of(30, TimeUnit.SECONDS); - public static final Duration ONE_MINUTE = of(1, TimeUnit.MINUTES); - public static final Duration TWO_MINUTES = of(2, TimeUnit.MINUTES); - public static final Duration FIVE_MINUTES = of(5, TimeUnit.MINUTES); - public static final Duration ONE_HOUR = of(1, TimeUnit.HOURS); - public static final Duration ONE_DAY = of(1, TimeUnit.DAYS); - - /** longest supported duration, 2^{63}-1 nanoseconds, approx ten billion seconds, or 300 years */ - public static final Duration PRACTICALLY_FOREVER = of(Long.MAX_VALUE, TimeUnit.NANOSECONDS); - - private final long nanos; - - public Duration(long value, TimeUnit unit) { - if (value != 0) { - Preconditions.checkNotNull(unit, "Cannot accept null timeunit (unless value is 0)"); - } else { - unit = TimeUnit.MILLISECONDS; - } - nanos = TimeUnit.NANOSECONDS.convert(value, unit); - } - - @Override - public int compareTo(Duration o) { - return ((Long)toNanoseconds()).compareTo(o.toNanoseconds()); - } - - @Override - public String toString() { - return Time.makeTimeStringExact(this); - } - - public String toStringRounded() { - return Time.makeTimeStringRounded(this); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Duration)) return false; - return toMilliseconds() == ((Duration)o).toMilliseconds(); - } - - @Override - public int hashCode() { - return Long.valueOf(toMilliseconds()).hashCode(); - } - - /** converts to the given {@link TimeUnit}, using {@link TimeUnit#convert(long, TimeUnit)} which rounds _down_ - * (so 1 nanosecond converted to milliseconds gives 0 milliseconds, and -1 ns gives -1 ms) */ - public long toUnit(TimeUnit unit) { - return unit.convert(nanos, TimeUnit.NANOSECONDS); - } - - /** as {@link #toUnit(TimeUnit)} but rounding as indicated - * (rather than always taking the floor which is TimeUnit's default behaviour) */ - public long toUnit(TimeUnit unit, RoundingMode rounding) { - long result = unit.convert(nanos, TimeUnit.NANOSECONDS); - long check = TimeUnit.NANOSECONDS.convert(result, unit); - if (check==nanos || rounding==null || rounding==RoundingMode.UNNECESSARY) return result; - return new BigDecimal(nanos).divide(new BigDecimal(unit.toNanos(1)), rounding).longValue(); - } - - /** as {@link #toUnit(TimeUnit)} but rounding away from zero, - * so 1 ns converted to ms gives 1 ms, and -1 ns gives 1ms */ - public long toUnitRoundingUp(TimeUnit unit) { - return toUnit(unit, RoundingMode.UP); - } - - public long toMilliseconds() { - return toUnit(TimeUnit.MILLISECONDS); - } - - /** as {@link #toMilliseconds()} but rounding away from zero (so 1 nanosecond gets rounded to 1 millisecond); - * see {@link #toUnitRoundingUp(TimeUnit)}; provided as a convenience on top of {@link #toUnit(TimeUnit, RoundingMode)} - * as this is a common case (when you want to make sure you wait at least a certain amount of time) */ - public long toMillisecondsRoundingUp() { - return toUnitRoundingUp(TimeUnit.MILLISECONDS); - } - - public long toNanoseconds() { - return nanos; - } - - public long toSeconds() { - return toUnit(TimeUnit.SECONDS); - } - - /** number of nanoseconds of this duration */ - public long nanos() { - return nanos; - } - - /** - * See {@link Time#parseElapsedTime(String)}; - * also accepts "forever" (and for those who prefer things exceedingly accurate, "practically_forever"). - * Also see {@link #of(Object)}. */ - public static Duration parse(String textualDescription) { - if (textualDescription==null) return null; - if ("null".equalsIgnoreCase(textualDescription)) return null; - - if ("forever".equalsIgnoreCase(textualDescription)) return Duration.PRACTICALLY_FOREVER; - if ("practicallyforever".equalsIgnoreCase(textualDescription)) return Duration.PRACTICALLY_FOREVER; - if ("practically_forever".equalsIgnoreCase(textualDescription)) return Duration.PRACTICALLY_FOREVER; - - return new Duration((long) Time.parseElapsedTimeAsDouble(textualDescription), TimeUnit.MILLISECONDS); - } - - /** creates new {@link Duration} instance of the given length of time */ - public static Duration days(Number n) { - return new Duration((long) (n.doubleValue() * TimeUnit.DAYS.toNanos(1)), TimeUnit.NANOSECONDS); - } - - /** creates new {@link Duration} instance of the given length of time */ - public static Duration hours(Number n) { - return new Duration((long) (n.doubleValue() * TimeUnit.HOURS.toNanos(1)), TimeUnit.NANOSECONDS); - } - - /** creates new {@link Duration} instance of the given length of time */ - public static Duration minutes(Number n) { - return new Duration((long) (n.doubleValue() * TimeUnit.MINUTES.toNanos(1)), TimeUnit.NANOSECONDS); - } - - /** creates new {@link Duration} instance of the given length of time */ - public static Duration seconds(Number n) { - return new Duration((long) (n.doubleValue() * TimeUnit.SECONDS.toNanos(1)), TimeUnit.NANOSECONDS); - } - - /** creates new {@link Duration} instance of the given length of time */ - public static Duration millis(Number n) { - return new Duration((long) (n.doubleValue() * TimeUnit.MILLISECONDS.toNanos(1)), TimeUnit.NANOSECONDS); - } - - /** creates new {@link Duration} instance of the given length of time */ - public static Duration nanos(Number n) { - return new Duration(n.longValue(), TimeUnit.NANOSECONDS); - } - - public static Function<Number, String> millisToStringRounded() { return millisToStringRounded; } - private static Function<Number, String> millisToStringRounded = new Function<Number, String>() { - @Override - @Nullable - public String apply(@Nullable Number input) { - if (input == null) return null; - return Duration.millis(input).toStringRounded(); - } - }; - - public static Function<Number, String> secondsToStringRounded() { return secondsToStringRounded; } - private static Function<Number, String> secondsToStringRounded = new Function<Number, String>() { - @Override - @Nullable - public String apply(@Nullable Number input) { - if (input == null) return null; - return Duration.seconds(input).toStringRounded(); - } - }; - - /** tries to convert given object to a Duration, parsing strings, treating numbers as millis, etc; - * throws IAE if not convertible */ - public static Duration of(Object o) { - if (o == null) return null; - if (o instanceof Duration) return (Duration)o; - if (o instanceof String) return parse((String)o); - if (o instanceof Number) return millis((Number)o); - if (o instanceof Stopwatch) return millis(((Stopwatch)o).elapsed(TimeUnit.MILLISECONDS)); - - try { - // this allows it to work with groovy TimeDuration - Method millisMethod = o.getClass().getMethod("toMilliseconds"); - return millis((Long)millisMethod.invoke(o)); - } catch (Exception e) { - // probably no such method - } - - throw new IllegalArgumentException("Cannot convert "+o+" (type "+o.getClass()+") to a duration"); - } - - public static Duration of(long value, TimeUnit unit) { - return new Duration(value, unit); - } - - public static Duration max(Duration first, Duration second) { - return checkNotNull(first, "first").nanos >= checkNotNull(second, "second").nanos ? first : second; - } - - public static Duration min(Duration first, Duration second) { - return checkNotNull(first, "first").nanos <= checkNotNull(second, "second").nanos ? first : second; - } - - public static Duration untilUtc(long millisSinceEpoch) { - return millis(millisSinceEpoch - System.currentTimeMillis()); - } - - public static Duration sinceUtc(long millisSinceEpoch) { - return millis(System.currentTimeMillis() - millisSinceEpoch); - } - - public Duration add(Duration other) { - return nanos(nanos() + other.nanos()); - } - - public Duration subtract(Duration other) { - return nanos(nanos() - other.nanos()); - } - - public Duration multiply(long x) { - return nanos(nanos() * x); - } - public Duration times(long x) { - return multiply(x); - } - - /** as #multiply(long), but approximate due to the division (nano precision) */ - public Duration multiply(double d) { - return nanos(nanos() * d); - } - - public Duration half() { - return multiply(0.5); - } - - /** see {@link Time#sleep(long)} */ - public static void sleep(Duration duration) { - Time.sleep(duration); - } - - /** returns a new started {@link CountdownTimer} with this duration */ - public CountdownTimer countdownTimer() { - return CountdownTimer.newInstanceStarted(this); - } - - public boolean isPositive() { - return nanos()>0; - } - - public boolean isLongerThan(Duration x) { - return compareTo(x) > 0; - } - - public boolean isLongerThan(Stopwatch stopwatch) { - return isLongerThan(Duration.millis(stopwatch.elapsed(TimeUnit.MILLISECONDS))); - } - - public boolean isShorterThan(Duration x) { - return compareTo(x) < 0; - } - - public boolean isShorterThan(Stopwatch stopwatch) { - return isShorterThan(Duration.millis(stopwatch.elapsed(TimeUnit.MILLISECONDS))); - } - - /** returns the larger of this value or the argument */ - public Duration lowerBound(Duration alternateMinimumValue) { - if (isShorterThan(alternateMinimumValue)) return alternateMinimumValue; - return this; - } - - /** returns the smaller of this value or the argument */ - public Duration upperBound(Duration alternateMaximumValue) { - if (isLongerThan(alternateMaximumValue)) return alternateMaximumValue; - return this; - } - - /** @deprecated since 0.7.0 use {@link #lowerBound(Duration)} */ @Deprecated - public Duration minimum(Duration alternateMinimumValue) { - return lowerBound(alternateMinimumValue); - } - - /** @deprecated since 0.7.0 use {@link #upperBound(Duration)} */ @Deprecated - /** returns the smaller of this value or the argument */ - public Duration maximum(Duration alternateMaximumValue) { - return upperBound(alternateMaximumValue); - } -}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/brooklyn/util/time/Durations.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/brooklyn/util/time/Durations.java b/utils/common/src/main/java/brooklyn/util/time/Durations.java deleted file mode 100644 index 21450a6..0000000 --- a/utils/common/src/main/java/brooklyn/util/time/Durations.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 brooklyn.util.time; - -import java.util.concurrent.CancellationException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import brooklyn.util.exceptions.Exceptions; -import brooklyn.util.guava.Maybe; - -public class Durations { - - public static boolean await(CountDownLatch latch, Duration time) throws InterruptedException { - return latch.await(time.toNanoseconds(), TimeUnit.NANOSECONDS); - } - - public static void join(Thread thread, Duration time) throws InterruptedException { - thread.join(time.toMillisecondsRoundingUp()); - } - - public static <T> Maybe<T> get(Future<T> t, Duration timeout) { - try { - if (timeout==null || timeout.toMilliseconds()<0 || Duration.PRACTICALLY_FOREVER.equals(timeout)) - return Maybe.of(t.get()); - if (timeout.toMilliseconds()==0 && !t.isDone()) { - for (int i=0; i<10; i++) { - // give it 10 nanoseconds to complete - heuristically this is often enough - // (Thread.yield should do it, but often seems to have no effect, e.g. on Mac) - Thread.yield(); - Thread.sleep(0, 1); - } - return Maybe.absent("Task "+t+" not completed when immediate completion requested"); - } - return Maybe.of(t.get(timeout.toMilliseconds(), TimeUnit.MILLISECONDS)); - } catch (TimeoutException e) { - return Maybe.absent("Task "+t+" did not complete within "+timeout); - } catch (CancellationException e) { - return Maybe.absent("Task "+t+" was cancelled"); - } catch (Exception e) { - throw Exceptions.propagate(e); - } - } - - public static <T> Maybe<T> get(Future<T> t, CountdownTimer timer) { - if (timer==null) return get(t, (Duration)null); - Duration remaining = timer.getDurationRemaining(); - if (remaining.isPositive()) return get(t, remaining); - return get(t, Duration.ZERO); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/brooklyn/util/time/Time.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/brooklyn/util/time/Time.java b/utils/common/src/main/java/brooklyn/util/time/Time.java deleted file mode 100644 index 3379d49..0000000 --- a/utils/common/src/main/java/brooklyn/util/time/Time.java +++ /dev/null @@ -1,962 +0,0 @@ -/* - * 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 brooklyn.util.time; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.ParsePosition; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.Locale; -import java.util.SimpleTimeZone; -import java.util.TimeZone; -import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nullable; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brooklyn.util.collections.MutableList; -import brooklyn.util.exceptions.Exceptions; -import brooklyn.util.guava.Maybe; -import brooklyn.util.text.Strings; - -import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.base.Stopwatch; - -public class Time { - - private static final Logger log = LoggerFactory.getLogger(Time.class); - - public static final String DATE_FORMAT_PREFERRED_W_TZ = "yyyy-MM-dd HH:mm:ss.SSS Z"; - public static final String DATE_FORMAT_PREFERRED = "yyyy-MM-dd HH:mm:ss.SSS"; - public static final String DATE_FORMAT_STAMP = "yyyyMMdd-HHmmssSSS"; - public static final String DATE_FORMAT_SIMPLE_STAMP = "yyyy-MM-dd-HHmm"; - public static final String DATE_FORMAT_OF_DATE_TOSTRING = "EEE MMM dd HH:mm:ss zzz yyyy"; - public static final String DATE_FORMAT_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; - public static final String DATE_FORMAT_ISO8601_NO_MILLIS = "yyyy-MM-dd'T'HH:mm:ssZ"; - - public static final long MILLIS_IN_SECOND = 1000; - public static final long MILLIS_IN_MINUTE = 60*MILLIS_IN_SECOND; - public static final long MILLIS_IN_HOUR = 60*MILLIS_IN_MINUTE; - public static final long MILLIS_IN_DAY = 24*MILLIS_IN_HOUR; - public static final long MILLIS_IN_YEAR = 365*MILLIS_IN_DAY; - - /** GMT/UTC/Z time zone constant */ - public static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone(""); - - /** as {@link #makeDateString(Date)} for current date/time */ - public static String makeDateString() { - return makeDateString(System.currentTimeMillis()); - } - - /** as {@link #makeDateString(Date)} for long millis since UTC epock */ - public static String makeDateString(long date) { - return makeDateString(new Date(date), DATE_FORMAT_PREFERRED); - } - /** returns the time in {@value #DATE_FORMAT_PREFERRED} format for the given date; - * this format is numeric big-endian but otherwise optimized for people to read, with spaces and colons and dots; - * time is local to the server and time zone is <i>not</i> included */ - public static String makeDateString(Date date) { - return makeDateString(date, DATE_FORMAT_PREFERRED); - } - /** as {@link #makeDateString(Date, String, TimeZone)} for the local time zone */ - public static String makeDateString(Date date, String format) { - return makeDateString(date, format, null); - } - /** as {@link #makeDateString(Date, String, TimeZone)} for the given time zone; consider {@link TimeZone#GMT} */ - public static String makeDateString(Date date, String format, TimeZone tz) { - SimpleDateFormat fmt = new SimpleDateFormat(format); - if (tz!=null) fmt.setTimeZone(tz); - return fmt.format(date); - } - /** as {@link #makeDateString(Date, String)} using {@link #DATE_FORMAT_PREFERRED_W_TZ} */ - public static String makeDateString(Calendar date) { - return makeDateString(date.getTime(), DATE_FORMAT_PREFERRED_W_TZ); - } - /** as {@link #makeDateString(Date, String, TimeZone)} for the time zone of the given calendar object */ - public static String makeDateString(Calendar date, String format) { - return makeDateString(date.getTime(), format, date.getTimeZone()); - } - - public static Function<Long, String> toDateString() { return dateString; } - private static Function<Long, String> dateString = new Function<Long, String>() { - @Override - @Nullable - public String apply(@Nullable Long input) { - if (input == null) return null; - return Time.makeDateString(input); - } - }; - - /** returns the current time in {@value #DATE_FORMAT_STAMP} format, - * suitable for machines to read with only numbers and dashes and quite precise (ms) */ - public static String makeDateStampString() { - return makeDateStampString(System.currentTimeMillis()); - } - - /** returns the time in {@value #DATE_FORMAT_STAMP} format, given a long (e.g. returned by System.currentTimeMillis); - * cf {@link #makeDateStampString()} */ - public static String makeDateStampString(long date) { - return new SimpleDateFormat(DATE_FORMAT_STAMP).format(new Date(date)); - } - - /** returns the current time in {@value #DATE_FORMAT_SIMPLE_STAMP} format, - * suitable for machines to read but easier for humans too, - * like {@link #makeDateStampString()} but not as precise */ - public static String makeDateSimpleStampString() { - return makeDateSimpleStampString(System.currentTimeMillis()); - } - - /** returns the time in {@value #DATE_FORMAT_SIMPLE_STAMP} format, given a long (e.g. returned by System.currentTimeMillis); - * cf {@link #makeDateSimpleStampString()} */ - public static String makeDateSimpleStampString(long date) { - return new SimpleDateFormat(DATE_FORMAT_SIMPLE_STAMP).format(new Date(date)); - } - - public static Function<Long, String> toDateStampString() { return dateStampString; } - private static Function<Long, String> dateStampString = new Function<Long, String>() { - @Override - @Nullable - public String apply(@Nullable Long input) { - if (input == null) return null; - return Time.makeDateStampString(input); - } - }; - - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringExact(long t, TimeUnit unit) { - long nanos = unit.toNanos(t); - return makeTimeStringNanoExact(nanos); - } - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringRounded(long t, TimeUnit unit) { - long nanos = unit.toNanos(t); - return makeTimeStringNanoRounded(nanos); - } - public static String makeTimeStringRounded(Stopwatch timer) { - return makeTimeStringRounded(timer.elapsed(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS); - } - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringExact(long t) { - return makeTimeString(t, false); - } - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringRounded(long t) { - return makeTimeString(t, true); - } - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringRoundedSince(long utc) { - return makeTimeString(System.currentTimeMillis() - utc, true); - } - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringExact(Duration d) { - return makeTimeStringNanoExact(d.toNanoseconds()); - } - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringRounded(Duration d) { - return makeTimeStringNanoRounded(d.toNanoseconds()); - } - /** given an elapsed time, makes it readable, eg 44d 6h, or 8s 923ms, optionally rounding */ - public static String makeTimeString(long t, boolean round) { - return makeTimeStringNano(t*1000000L, round); - } - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringNanoExact(long tn) { - return makeTimeStringNano(tn, false); - } - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringNanoRounded(long tn) { - return makeTimeStringNano(tn, true); - } - /** @see #makeTimeString(long, boolean) */ - public static String makeTimeStringNano(long tn, boolean round) { - if (tn<0) return "-"+makeTimeStringNano(-tn, round); - // units don't matter, but since ms is the usual finest granularity let's use it - // (previously was just "0" but that was too ambiguous in contexts like "took 0") - if (tn==0) return "0ms"; - - long tnm = tn % 1000000; - long t = tn/1000000; - String result = ""; - - long d = t/MILLIS_IN_DAY; t %= MILLIS_IN_DAY; - long h = t/MILLIS_IN_HOUR; t %= MILLIS_IN_HOUR; - long m = t/MILLIS_IN_MINUTE; t %= MILLIS_IN_MINUTE; - long s = t/MILLIS_IN_SECOND; t %= MILLIS_IN_SECOND; - long ms = t; - - int segments = 0; - if (d>0) { result += d+"d "; segments++; } - if (h>0) { result += h+"h "; segments++; } - if (round && segments>=2) return Strings.removeAllFromEnd(result, " "); - if (m>0) { result += m+"m "; segments++; } - if (round && (segments>=2 || d>0)) return Strings.removeAllFromEnd(result, " "); - if (s>0) { - if (ms==0 && tnm==0) { - result += s+"s"; segments++; - return result; - } - if (round && segments>0) { - result += s+"s"; segments++; - return result; - } - if (round && s>10) { - result += toDecimal(s, ms/1000.0, 1)+"s"; segments++; - return result; - } - if (round) { - result += toDecimal(s, ms/1000.0, 2)+"s"; segments++; - return result; - } - result += s+"s "; - } - if (round && segments>0) - return Strings.removeAllFromEnd(result, " "); - if (ms>0) { - if (tnm==0) { - result += ms+"ms"; segments++; - return result; - } - if (round && ms>=100) { - result += toDecimal(ms, tnm/1000000.0, 1)+"ms"; segments++; - return result; - } - if (round && ms>=10) { - result += toDecimal(ms, tnm/1000000.0, 2)+"ms"; segments++; - return result; - } - if (round) { - result += toDecimal(ms, tnm/1000000.0, 3)+"ms"; segments++; - return result; - } - result += ms+"ms "; - } - - long us = tnm/1000; - long ns = tnm % 1000; - - if (us>0) { - if (ns==0) { - result += us+"us"; segments++; - return result; - } - if (round && us>=100) { - result += toDecimal(us, ns/1000.0, 1)+"us"; segments++; - return result; - } - if (round && us>=10) { - result += toDecimal(us, ns/1000.0, 2)+"us"; segments++; - return result; - } - if (round) { - result += toDecimal(us, ns/1000.0, 3)+"us"; segments++; - return result; - } - result += us+"us "; - } - - if (ns>0) result += ns+"ns"; - return Strings.removeAllFromEnd(result, " "); - } - - public static Function<Long, String> fromLongToTimeStringExact() { return LONG_TO_TIME_STRING_EXACT; } - private static final Function<Long, String> LONG_TO_TIME_STRING_EXACT = new FunctionLongToTimeStringExact(); - private static final class FunctionLongToTimeStringExact implements Function<Long, String> { - @Override @Nullable - public String apply(@Nullable Long input) { - if (input == null) return null; - return Time.makeTimeStringExact(input); - } - } - - /** @deprecated since 0.7.0 use {@link #fromLongToTimeStringExact()} */ @Deprecated - public static Function<Long, String> toTimeString() { return timeString; } - @Deprecated - private static Function<Long, String> timeString = new Function<Long, String>() { - @Override - @Nullable - public String apply(@Nullable Long input) { - if (input == null) return null; - return Time.makeTimeStringExact(input); - } - }; - - public static Function<Long, String> fromLongToTimeStringRounded() { return LONG_TO_TIME_STRING_ROUNDED; } - private static final Function<Long, String> LONG_TO_TIME_STRING_ROUNDED = new FunctionLongToTimeStringRounded(); - private static final class FunctionLongToTimeStringRounded implements Function<Long, String> { - @Override @Nullable - public String apply(@Nullable Long input) { - if (input == null) return null; - return Time.makeTimeStringRounded(input); - } - } - - /** @deprecated since 0.7.0 use {@link #fromLongToTimeStringRounded()} */ @Deprecated - public static Function<Long, String> toTimeStringRounded() { return timeStringRounded; } - @Deprecated - private static Function<Long, String> timeStringRounded = new Function<Long, String>() { - @Override - @Nullable - public String apply(@Nullable Long input) { - if (input == null) return null; - return Time.makeTimeStringRounded(input); - } - }; - - public static Function<Duration, String> fromDurationToTimeStringRounded() { return DURATION_TO_TIME_STRING_ROUNDED; } - private static final Function<Duration, String> DURATION_TO_TIME_STRING_ROUNDED = new FunctionDurationToTimeStringRounded(); - private static final class FunctionDurationToTimeStringRounded implements Function<Duration, String> { - @Override @Nullable - public String apply(@Nullable Duration input) { - if (input == null) return null; - return Time.makeTimeStringRounded(input); - } - } - - private static String toDecimal(long intPart, double fracPart, int decimalPrecision) { - long powTen = 1; - for (int i=0; i<decimalPrecision; i++) powTen *= 10; - long fpr = Math.round(fracPart * powTen); - if (fpr==powTen) { - intPart++; - fpr = 0; - } - return intPart + "." + Strings.makePaddedString(""+fpr, decimalPrecision, "0", ""); - } - - /** sleep which propagates Interrupted as unchecked */ - public static void sleep(long millis) { - try { - if (millis > 0) Thread.sleep(millis); - } catch (InterruptedException e) { - throw Exceptions.propagate(e); - } - } - - /** as {@link #sleep(long)} */ - public static void sleep(Duration duration) { - Time.sleep(duration.toMillisecondsRoundingUp()); - } - - /** - * Calculates the number of milliseconds past midnight for a given UTC time. - */ - public static long getTimeOfDayFromUtc(long timeUtc) { - GregorianCalendar gregorianCalendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - gregorianCalendar.setTimeInMillis(timeUtc); - int hour = gregorianCalendar.get(Calendar.HOUR_OF_DAY); - int min = gregorianCalendar.get(Calendar.MINUTE); - int sec = gregorianCalendar.get(Calendar.SECOND); - int millis = gregorianCalendar.get(Calendar.MILLISECOND); - return (((((hour * 60) + min) * 60) + sec) * 1000) + millis; - } - - /** - * Calculates the number of milliseconds past epoch for a given UTC time. - */ - public static long getTimeUtc(TimeZone zone, int year, int month, int date, int hourOfDay, int minute, int second, int millis) { - GregorianCalendar time = new GregorianCalendar(zone); - time.set(year, month, date, hourOfDay, minute, second); - time.set(Calendar.MILLISECOND, millis); - return time.getTimeInMillis(); - } - - public static long roundFromMillis(long millis, TimeUnit units) { - if (units.compareTo(TimeUnit.MILLISECONDS) > 0) { - double result = ((double)millis) / units.toMillis(1); - return Math.round(result); - } else { - return units.convert(millis, TimeUnit.MILLISECONDS); - } - } - - public static long roundFromMillis(long millis, long millisPerUnit) { - double result = ((double)millis) / millisPerUnit; - return Math.round(result); - } - - /** - * Calculates how long until maxTime has passed since the given startTime. - * However, maxTime==0 is a special case (e.g. could mean wait forever), so the result is guaranteed - * to be only 0 if maxTime was 0; otherwise -1 will be returned. - */ - public static long timeRemaining(long startTime, long maxTime) { - if (maxTime == 0) { - return 0; - } - long result = (startTime+maxTime) - System.currentTimeMillis(); - return (result == 0) ? -1 : result; - } - - /** Convenience for {@link Duration#parse(String)}. */ - public static Duration parseDuration(String timeString) { - return Duration.parse(timeString); - } - - /** - * As {@link #parseElapsedTimeAsDouble(String)}. Consider using {@link #parseDuration(String)} for a more usable return type. - * - * @throws NumberFormatException if cannot be parsed (or if null) - */ - public static long parseElapsedTime(String timeString) { - return (long) parseElapsedTimeAsDouble(timeString); - } - /** @deprecated since 0.7.0 see {@link #parseElapsedTime(String)} */ @Deprecated - public static long parseTimeString(String timeString) { - return (long) parseElapsedTime(timeString); - } - /** @deprecated since 0.7.0 see {@link #parseElapsedTimeAsDouble(String)} */ @Deprecated - public static double parseTimeStringAsDouble(String timeString) { - return parseElapsedTimeAsDouble(timeString); - } - - /** - * Parses a string eg '5s' or '20m 22.123ms', returning the number of milliseconds it represents; - * -1 on blank or never or off or false. - * Assumes unit is millisections if no unit is specified. - * - * @throws NumberFormatException if cannot be parsed (or if null) - */ - public static double parseElapsedTimeAsDouble(String timeString) { - if (timeString==null) - throw new NumberFormatException("GeneralHelper.parseTimeString cannot parse a null string"); - try { - double d = Double.parseDouble(timeString); - return d; - } catch (NumberFormatException e) { - //look for a type marker - timeString = timeString.trim(); - String s = Strings.getLastWord(timeString).toLowerCase(); - timeString = timeString.substring(0, timeString.length()-s.length()).trim(); - int i=0; - while (s.length()>i) { - char c = s.charAt(i); - if (c=='.' || Character.isDigit(c)) i++; - else break; - } - String num = s.substring(0, i); - if (i==0) { - num = Strings.getLastWord(timeString).toLowerCase(); - timeString = timeString.substring(0, timeString.length()-num.length()).trim(); - } else { - s = s.substring(i); - } - long multiplier = 0; - if (num.length()==0) { - //must be never or something - if (s.equalsIgnoreCase("never") || s.equalsIgnoreCase("off") || s.equalsIgnoreCase("false")) - return -1; - throw new NumberFormatException("unrecognised word '"+s+"' in time string"); - } - if (s.equalsIgnoreCase("ms") || s.equalsIgnoreCase("milli") || s.equalsIgnoreCase("millis") - || s.equalsIgnoreCase("millisec") || s.equalsIgnoreCase("millisecs") - || s.equalsIgnoreCase("millisecond") || s.equalsIgnoreCase("milliseconds")) - multiplier = 1; - else if (s.equalsIgnoreCase("s") || s.equalsIgnoreCase("sec") || s.equalsIgnoreCase("secs") - || s.equalsIgnoreCase("second") || s.equalsIgnoreCase("seconds")) - multiplier = 1000; - else if (s.equalsIgnoreCase("m") || s.equalsIgnoreCase("min") || s.equalsIgnoreCase("mins") - || s.equalsIgnoreCase("minute") || s.equalsIgnoreCase("minutes")) - multiplier = 60*1000; - else if (s.equalsIgnoreCase("h") || s.equalsIgnoreCase("hr") || s.equalsIgnoreCase("hrs") - || s.equalsIgnoreCase("hour") || s.equalsIgnoreCase("hours")) - multiplier = 60*60*1000; - else if (s.equalsIgnoreCase("d") || s.equalsIgnoreCase("day") || s.equalsIgnoreCase("days")) - multiplier = 24*60*60*1000; - else - throw new NumberFormatException("unknown unit '"+s+"' in time string"); - double d = Double.parseDouble(num); - double dd = 0; - if (timeString.length()>0) { - dd = parseElapsedTimeAsDouble(timeString); - if (dd==-1) { - throw new NumberFormatException("cannot combine '"+timeString+"' with '"+num+" "+s+"'"); - } - } - return d*multiplier + dd; - } - } - - public static Calendar newCalendarFromMillisSinceEpochUtc(long timestamp) { - GregorianCalendar cal = new GregorianCalendar(); - cal.setTimeInMillis(timestamp); - return cal; - } - - public static Calendar newCalendarFromDate(Date date) { - return newCalendarFromMillisSinceEpochUtc(date.getTime()); - } - - /** As {@link #parseCalendar(String)} but returning a {@link Date}, - * (i.e. a record where the time zone has been applied and forgotten). */ - public static Date parseDate(String input) { - if (input==null) return null; - return parseCalendarMaybe(input).get().getTime(); - } - - /** Parses dates from string, accepting many formats including ISO-8601 and http://yaml.org/type/timestamp.html, - * e.g. 2015-06-15 16:00:00 +0000. - * <p> - * Millis since epoch (1970) is also supported to represent the epoch (0) or dates in this millenium, - * but to prevent ambiguity of e.g. "20150615", any other dates prior to the year 2001 are not accepted. - * (However if a type Long is supplied, e.g. from a YAML parse, it will always be treated as millis since epoch.) - * <p> - * Other formats including locale-specific variants, e.g. recognising month names, - * are supported but this may vary from platform to platform and may change between versions. */ - public static Calendar parseCalendar(String input) { - if (input==null) return null; - return parseCalendarMaybe(input).get(); - } - - /** as {@link #parseCalendar(String)} but returning a {@link Maybe} rather than throwing or returning null */ - public static Maybe<Calendar> parseCalendarMaybe(String input) { - if (input==null) return Maybe.absent("value is null"); - input = input.trim(); - Maybe<Calendar> result; - - result = parseCalendarUtc(input); - if (result.isPresent()) return result; - - result = parseCalendarSimpleFlexibleFormatParser(input); - if (result.isPresent()) return result; - // return the error from this method - Maybe<Calendar> returnResult = result; - - result = parseCalendarFormat(input, new SimpleDateFormat(DATE_FORMAT_OF_DATE_TOSTRING, Locale.ROOT)); - if (result.isPresent()) return result; - result = parseCalendarDefaultParse(input); - if (result.isPresent()) return result; - - return returnResult; - } - - @SuppressWarnings("deprecation") - private static Maybe<Calendar> parseCalendarDefaultParse(String input) { - try { - long ms = Date.parse(input); - if (ms>=new Date(1999, 12, 25).getTime() && ms <= new Date(2200, 1, 2).getTime()) { - // accept default date parse for this century and next - GregorianCalendar c = new GregorianCalendar(); - c.setTimeInMillis(ms); - return Maybe.of((Calendar)c); - } - } catch (Exception e) { - Exceptions.propagateIfFatal(e); - } - return Maybe.absent(); - } - - private static Maybe<Calendar> parseCalendarUtc(String input) { - input = input.trim(); - if (input.matches("\\d+")) { - if ("0".equals(input)) { - // accept 0 as epoch UTC - return Maybe.of(newCalendarFromMillisSinceEpochUtc(0)); - } - Maybe<Calendar> result = Maybe.of(newCalendarFromMillisSinceEpochUtc(Long.parseLong(input))); - if (result.isPresent()) { - int year = result.get().get(Calendar.YEAR); - if (year >= 2000 && year < 2200) { - // only applicable for dates in this century - return result; - } else { - return Maybe.absent("long is probably not millis since epoch UTC; millis as string is not in acceptable range"); - } - } - } - return Maybe.absent("not long millis since epoch UTC"); - } - - private final static String DIGIT = "\\d"; - private final static String LETTER = "\\p{L}"; - private final static String COMMON_SEPARATORS = "-\\."; - private final static String TIME_SEPARATOR = COMMON_SEPARATORS+":"; - private final static String DATE_SEPARATOR = COMMON_SEPARATORS+"/ "; - private final static String DATE_TIME_ANY_ORDER_GROUP_SEPARATOR = COMMON_SEPARATORS+":/ "; - - private final static String DATE_ONLY_WITH_INNER_SEPARATORS = - namedGroup("year", DIGIT+DIGIT+DIGIT+DIGIT) + - anyChar(DATE_SEPARATOR) + - namedGroup("month", options(optionally(DIGIT)+DIGIT, anyChar(LETTER)+"+")) + - anyChar(DATE_SEPARATOR) + - namedGroup("day", optionally(DIGIT)+DIGIT); - private final static String DATE_WORDS_2 = - namedGroup("month", anyChar(LETTER)+"+") + - anyChar(DATE_SEPARATOR) + - namedGroup("day", optionally(DIGIT)+DIGIT) + - ",?"+anyChar(DATE_SEPARATOR)+"+" + - namedGroup("year", DIGIT+DIGIT+DIGIT+DIGIT); - // we could parse NN-NN-NNNN as DD-MM-YYYY always, but could be confusing for MM-DD-YYYY oriented people, so require month named - private final static String DATE_WORDS_3 = - namedGroup("day", optionally(DIGIT)+DIGIT) + - anyChar(DATE_SEPARATOR) + - namedGroup("month", anyChar(LETTER)+"+") + - ",?"+anyChar(DATE_SEPARATOR)+"+" + - namedGroup("year", DIGIT+DIGIT+DIGIT+DIGIT); - - private final static String DATE_ONLY_NO_SEPARATORS = - namedGroup("year", DIGIT+DIGIT+DIGIT+DIGIT) + - namedGroup("month", DIGIT+DIGIT) + - namedGroup("day", DIGIT+DIGIT); - - private final static String MERIDIAN = anyChar("aApP")+optionally(anyChar("mM")); - private final static String TIME_ONLY_WITH_INNER_SEPARATORS = - namedGroup("hours", optionally(DIGIT)+DIGIT)+ - optionally( - anyChar(TIME_SEPARATOR)+ - namedGroup("mins", DIGIT+DIGIT)+ - optionally( - anyChar(TIME_SEPARATOR)+ - namedGroup("secs", DIGIT+DIGIT+optionally( optionally("\\.")+DIGIT+"+"))))+ - optionally(" *" + namedGroup("meridian", notMatching(LETTER+LETTER+LETTER)+MERIDIAN)); - private final static String TIME_ONLY_NO_SEPARATORS = - namedGroup("hours", DIGIT+DIGIT)+ - namedGroup("mins", DIGIT+DIGIT)+ - optionally( - namedGroup("secs", DIGIT+DIGIT+optionally( optionally("\\.")+DIGIT+"+")))+ - namedGroup("meridian", ""); - - private final static String TZ_CODE = - namedGroup("tzCode", - notMatching(MERIDIAN+options("$", anyChar("^"+LETTER))) + // not AM or PM - anyChar(LETTER)+"+"+anyChar(LETTER+DIGIT+"\\/\\-\\' _")+"*"); - private final static String TIME_ZONE_SIGNED_OFFSET = - namedGroup("tz", - options( - namedGroup("tzOffset", options("\\+", "-")+ - DIGIT+optionally(DIGIT)+optionally(optionally(":")+DIGIT+DIGIT)), - optionally("\\+")+TZ_CODE)); - private final static String TIME_ZONE_OPTIONALLY_SIGNED_OFFSET = - namedGroup("tz", - options( - namedGroup("tzOffset", options("\\+", "-", " ")+ - options("0"+DIGIT, "10", "11", "12")+optionally(optionally(":")+DIGIT+DIGIT)), - TZ_CODE)); - - private static String getDateTimeSeparatorPattern(String extraChars) { - return - options( - " +"+optionally(anyChar(DATE_TIME_ANY_ORDER_GROUP_SEPARATOR+extraChars+",")), - anyChar(DATE_TIME_ANY_ORDER_GROUP_SEPARATOR+extraChars+",")) + - anyChar(DATE_TIME_ANY_ORDER_GROUP_SEPARATOR+extraChars)+"*"; - } - - @SuppressWarnings("deprecation") - // we have written our own parsing because the alternatives were either too specific or too general - // java and apache and even joda-time are too specific, and would require explosion of patterns to be flexible; - // Natty - https://github.com/joestelmach/natty - is very cool, but it drags in ANTLR, - // it doesn't support dashes between date and time, and - // it encourages relative time which would be awesome but only if we resolved it on read - // (however there is natty code to parseDateNatty in the git history if we did want to use it) - private static Maybe<Calendar> parseCalendarSimpleFlexibleFormatParser(String input) { - input = input.trim(); - - String[] DATE_PATTERNS = new String[] { - DATE_ONLY_WITH_INNER_SEPARATORS, - DATE_ONLY_NO_SEPARATORS, - DATE_WORDS_2, - DATE_WORDS_3, - }; - String[] TIME_PATTERNS = new String[] { - TIME_ONLY_WITH_INNER_SEPARATORS, - TIME_ONLY_NO_SEPARATORS - }; - String[] TZ_PATTERNS = new String[] { - // space then time zone with sign (+-) or code is preferred - optionally(getDateTimeSeparatorPattern("")) + " " + TIME_ZONE_SIGNED_OFFSET, - // then no TZ - but declare the named groups - namedGroup("tz", namedGroup("tzOffset", "")+namedGroup("tzCode", "")), - // then any separator then offset with sign - getDateTimeSeparatorPattern("") + TIME_ZONE_SIGNED_OFFSET, - - // try parsing with enforced separators before TZ first - // (so e.g. in the case of DATE-0100, the -0100 is the time, not the timezone) - // then relax below (e.g. in the case of DATE-TIME+0100) - - // finally match DATE-TIME-1000 as time zone -1000 - // or DATE-TIME 1000 as TZ +1000 in case a + was supplied but converted to ' ' by web - // (but be stricter about the format, two or four digits required, and hours <= 12 so as not to confuse with a year) - optionally(getDateTimeSeparatorPattern("")) + TIME_ZONE_OPTIONALLY_SIGNED_OFFSET - }; - - List<String> basePatterns = MutableList.of(); - - // patterns with date first - String[] DATE_PATTERNS_UNCLOSED = new String[] { - // separator before time *required* if date had separators - DATE_ONLY_WITH_INNER_SEPARATORS + "("+getDateTimeSeparatorPattern("Tt"), - // separator before time optional if date did not have separators - DATE_ONLY_NO_SEPARATORS + "("+optionally(getDateTimeSeparatorPattern("Tt")), - // separator before time required if date has words - DATE_WORDS_2 + "("+getDateTimeSeparatorPattern("Tt"), - DATE_WORDS_3 + "("+getDateTimeSeparatorPattern("Tt"), - }; - for (String tzP: TZ_PATTERNS) - for (String dateP: DATE_PATTERNS_UNCLOSED) - for (String timeP: TIME_PATTERNS) - basePatterns.add(dateP + timeP+")?" + tzP); - - // also allow time first, with TZ after, then before - for (String tzP: TZ_PATTERNS) - for (String dateP: DATE_PATTERNS) - for (String timeP: TIME_PATTERNS) - basePatterns.add(timeP + getDateTimeSeparatorPattern("") + dateP + tzP); - // also allow time first, with TZ after, then before - for (String tzP: TZ_PATTERNS) - for (String dateP: DATE_PATTERNS) - for (String timeP: TIME_PATTERNS) - basePatterns.add(timeP + tzP + getDateTimeSeparatorPattern("") + dateP); - - Maybe<Matcher> mm = Maybe.absent(); - for (String p: basePatterns) { - mm = match(p, input); - if (mm.isPresent()) break; - } - if (mm.isPresent()) { - Matcher m = mm.get(); - Calendar result; - - String tz = m.group("tz"); - - int year = Integer.parseInt(m.group("year")); - int day = Integer.parseInt(m.group("day")); - - String monthS = m.group("month"); - int month; - if (monthS.matches(DIGIT+"+")) { - month = Integer.parseInt(monthS)-1; - } else { - try { - month = new SimpleDateFormat("yyyy-MMM-dd", Locale.ROOT).parse("2015-"+monthS+"-15").getMonth(); - } catch (ParseException e) { - return Maybe.absent("Unknown date format '"+input+"': invalid month '"+monthS+"'; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); - } - } - - if (Strings.isNonBlank(tz)) { - TimeZone tzz = null; - String tzCode = m.group("tzCode"); - if (Strings.isNonBlank(tzCode)) { - tz = tzCode; - } - if (tz.matches(DIGIT+"+")) { - // stick a plus in front in case it was submitted by a web form and turned into a space - tz = "+"+tz; - } else { - tzz = getTimeZone(tz); - } - if (tzz==null) { - Maybe<Matcher> tmm = match(" ?(?<tzH>(\\+|\\-||)"+DIGIT+optionally(DIGIT)+")"+optionally(optionally(":")+namedGroup("tzM", DIGIT+DIGIT)), tz); - if (tmm.isAbsent()) { - return Maybe.absent("Unknown date format '"+input+"': invalid timezone '"+tz+"'; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); - } - Matcher tm = tmm.get(); - String tzM = tm.group("tzM"); - int offset = (60*Integer.parseInt(tm.group("tzH")) + Integer.parseInt("0"+(tzM!=null ? tzM : "")))*60; - tzz = new SimpleTimeZone(offset*1000, tz); - } - tz = getTimeZoneOffsetString(tzz, year, month, day); - result = new GregorianCalendar(tzz); - } else { - result = new GregorianCalendar(); - } - result.clear(); - - result.set(Calendar.YEAR, year); - result.set(Calendar.MONTH, month); - result.set(Calendar.DAY_OF_MONTH, day); - if (m.group("hours")!=null) { - int hours = Integer.parseInt(m.group("hours")); - String meridian = m.group("meridian"); - if (Strings.isNonBlank(meridian) && meridian.toLowerCase().startsWith("p")) { - if (hours>12) { - return Maybe.absent("Unknown date format '"+input+"': can't be "+hours+" PM; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); - } - hours += 12; - } - result.set(Calendar.HOUR_OF_DAY, hours); - String minsS = m.group("mins"); - if (Strings.isNonBlank(minsS)) { - result.set(Calendar.MINUTE, Integer.parseInt(minsS)); - } - String secsS = m.group("secs"); - if (Strings.isBlank(secsS)) { - // leave at zero - } else if (secsS.matches(DIGIT+DIGIT+"?")) { - result.set(Calendar.SECOND, Integer.parseInt(secsS)); - } else { - double s = Double.parseDouble(secsS); - if (secsS.indexOf('.')>=0) { - // accept - } else if (secsS.length()==5) { - // allow ssSSS with no punctuation - s = s/=1000; - } else { - return Maybe.absent("Unknown date format '"+input+"': invalid seconds '"+secsS+"'; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); - } - result.set(Calendar.SECOND, (int)s); - result.set(Calendar.MILLISECOND, (int)((s*1000) % 1000)); - } - } - - return Maybe.of(result); - } - return Maybe.absent("Unknown date format '"+input+"'; try http://yaml.org/type/timestamp.html format e.g. 2015-06-15 16:00:00 +0000"); - } - - public static TimeZone getTimeZone(String code) { - if (code.indexOf('/')==-1) { - if ("Z".equals(code)) return TIME_ZONE_UTC; - if ("UTC".equals(code)) return TIME_ZONE_UTC; - if ("GMT".equals(code)) return TIME_ZONE_UTC; - - // get the time zone -- most short codes aren't accepted, so accept (and prefer) certain common codes - if ("EST".equals(code)) return getTimeZone("America/New_York"); - if ("EDT".equals(code)) return getTimeZone("America/New_York"); - if ("PST".equals(code)) return getTimeZone("America/Los_Angeles"); - if ("PDT".equals(code)) return getTimeZone("America/Los_Angeles"); - if ("CST".equals(code)) return getTimeZone("America/Chicago"); - if ("CDT".equals(code)) return getTimeZone("America/Chicago"); - if ("MST".equals(code)) return getTimeZone("America/Denver"); - if ("MDT".equals(code)) return getTimeZone("America/Denver"); - - if ("BST".equals(code)) return getTimeZone("Europe/London"); // otherwise BST is Bangladesh! - if ("CEST".equals(code)) return getTimeZone("Europe/Paris"); - // IST falls through to below, where it is treated as India (not Irish); IDT not recognised - } - - TimeZone tz = TimeZone.getTimeZone(code); - if (tz!=null && !tz.equals(TimeZone.getTimeZone("GMT"))) { - // recognized - return tz; - } - // possibly unrecognized -- GMT returned if not known, bad TimeZone API! - String timeZones[] = TimeZone.getAvailableIDs(); - for (String tzs: timeZones) { - if (tzs.equals(code)) return tz; - } - // definitely unrecognized - return null; - } - - /** convert a TimeZone e.g. Europe/London to an offset string as at the given day, e.g. +0100 or +0000 depending daylight savings, - * absent with nice error if zone unknown */ - public static Maybe<String> getTimeZoneOffsetString(String tz, int year, int month, int day) { - TimeZone tzz = getTimeZone(tz); - if (tzz==null) return Maybe.absent("Unknown time zone code: "+tz); - return Maybe.of(getTimeZoneOffsetString(tzz, year, month, day)); - } - - /** as {@link #getTimeZoneOffsetString(String, int, int, int)} where the {@link TimeZone} is already instantiated */ - @SuppressWarnings("deprecation") - public static String getTimeZoneOffsetString(TimeZone tz, int year, int month, int day) { - int tzMins = tz.getOffset(new Date(year, month, day).getTime())/60/1000; - String tzStr = (tzMins<0 ? "-" : "+") + Strings.makePaddedString(""+(Math.abs(tzMins)/60), 2, "0", "")+Strings.makePaddedString(""+(Math.abs(tzMins)%60), 2, "0", ""); - return tzStr; - } - - private static String namedGroup(String name, String pattern) { - return "(?<"+name+">"+pattern+")"; - } - private static String anyChar(String charSet) { - return "["+charSet+"]"; - } - private static String optionally(String pattern) { - return "("+pattern+")?"; - } - private static String options(String ...patterns) { - return "("+Strings.join(patterns,"|")+")"; - } - private static String notMatching(String pattern) { - return "(?!"+pattern+")"; - } - - private static Maybe<Matcher> match(String pattern, String input) { - Matcher m = Pattern.compile("^"+pattern+"$").matcher(input); - if (m.find()) return Maybe.of(m); - return Maybe.absent(); - } - - /** - * Parses the given date, accepting either a UTC timestamp (i.e. a long), or a formatted date. - * <p> - * If no time zone supplied, this defaults to the TZ configured at the brooklyn server. - * - * @deprecated since 0.7.0 use {@link #parseCalendar(String)} for general or {@link #parseCalendarFormat(String, DateFormat)} for a format, - * plus {@link #parseCalendarUtc(String)} if you want to accept UTC - */ - public static Date parseDateString(String dateString, DateFormat format) { - Maybe<Calendar> r = parseCalendarFormat(dateString, format); - if (r.isPresent()) return r.get().getTime(); - - r = parseCalendarUtc(dateString); - if (r.isPresent()) return r.get().getTime(); - - throw new IllegalArgumentException("Date " + dateString + " cannot be parsed as UTC millis or using format " + format); - } - public static Maybe<Calendar> parseCalendarFormat(String dateString, String format) { - return parseCalendarFormat(dateString, new SimpleDateFormat(format, Locale.ROOT)); - } - public static Maybe<Calendar> parseCalendarFormat(String dateString, DateFormat format) { - if (dateString == null) { - throw new NumberFormatException("GeneralHelper.parseDateString cannot parse a null string"); - } - Preconditions.checkNotNull(format, "date format"); - dateString = dateString.trim(); - - ParsePosition p = new ParsePosition(0); - Date result = format.parse(dateString, p); - if (result!=null) { - // accept results even if the entire thing wasn't parsed, as enough was to match the requested format - return Maybe.of(newCalendarFromDate(result)); - } - if (log.isTraceEnabled()) log.trace("Could not parse date "+dateString+" using format "+format+": "+p); - return Maybe.absent(); - } - - /** removes milliseconds from the date object; needed if serializing to ISO-8601 format - * and want to serialize back and get the same data */ - public static Date dropMilliseconds(Date date) { - return date==null ? null : date.getTime()%1000!=0 ? new Date(date.getTime() - (date.getTime()%1000)) : date; - } - - /** returns the duration elapsed since the given timestamp (UTC) */ - public static Duration elapsedSince(long timestamp) { - return Duration.millis(System.currentTimeMillis() - timestamp); - } - - /** true iff it has been longer than the given duration since the given timestamp */ - public static boolean hasElapsedSince(long timestamp, Duration duration) { - return elapsedSince(timestamp).compareTo(duration) > 0; - } - - /** more readable and shorter convenience for System.currentTimeMillis() */ - public static long now() { - return System.currentTimeMillis(); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java b/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java deleted file mode 100644 index b46e9d1..0000000 --- a/utils/common/src/main/java/brooklyn/util/yaml/Yamls.java +++ /dev/null @@ -1,553 +0,0 @@ -/* - * 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 brooklyn.util.yaml; - -import java.io.Reader; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.annotation.Nullable; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.error.Mark; -import org.yaml.snakeyaml.nodes.MappingNode; -import org.yaml.snakeyaml.nodes.Node; -import org.yaml.snakeyaml.nodes.NodeId; -import org.yaml.snakeyaml.nodes.NodeTuple; -import org.yaml.snakeyaml.nodes.ScalarNode; -import org.yaml.snakeyaml.nodes.SequenceNode; - -import brooklyn.util.collections.Jsonya; -import brooklyn.util.collections.MutableList; -import brooklyn.util.exceptions.Exceptions; -import brooklyn.util.exceptions.UserFacingException; -import brooklyn.util.text.Strings; - -import com.google.common.annotations.Beta; -import com.google.common.collect.Iterables; - -public class Yamls { - - private static final Logger log = LoggerFactory.getLogger(Yamls.class); - - /** returns the given (yaml-parsed) object as the given yaml type. - * <p> - * if the object is an iterable or iterator this method will fully expand it as a list. - * if the requested type is not an iterable or iterator, and the list contains a single item, this will take that single item. - * <p> - * in other cases this method simply does a type-check and cast (no other type coercion). - * <p> - * @throws IllegalArgumentException if the input is an iterable not containing a single element, - * and the cast is requested to a non-iterable type - * @throws ClassCastException if cannot be casted */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static <T> T getAs(Object x, Class<T> type) { - if (x==null) return null; - if (x instanceof Iterable || x instanceof Iterator) { - List result = new ArrayList(); - Iterator xi; - if (Iterator.class.isAssignableFrom(x.getClass())) { - xi = (Iterator)x; - } else { - xi = ((Iterable)x).iterator(); - } - while (xi.hasNext()) { - result.add( xi.next() ); - } - if (type.isAssignableFrom(List.class)) return (T)result; - if (type.isAssignableFrom(Iterator.class)) return (T)result.iterator(); - x = Iterables.getOnlyElement(result); - } - if (type.isInstance(x)) return (T)x; - throw new ClassCastException("Cannot convert "+x+" ("+x.getClass()+") to "+type); - } - - /** - * Parses the given yaml, and walks the given path to return the referenced object. - * - * @see #getAt(Object, List) - */ - @Beta - public static Object getAt(String yaml, List<String> path) { - Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml); - Object current = result.iterator().next(); - return getAtPreParsed(current, path); - } - - /** - * For pre-parsed yaml, walks the maps/lists to return the given sub-item. - * In the given path: - * <ul> - * <li>A vanilla string is assumed to be a key into a map. - * <li>A string in the form like "[0]" is assumed to be an index into a list - * </ul> - * - * Also see {@link Jsonya}, such as {@code Jsonya.of(current).at(path).get()}. - * - * @return The object at the given path, or {@code null} if that path does not exist. - */ - @Beta - @SuppressWarnings("unchecked") - public static Object getAtPreParsed(Object current, List<String> path) { - for (String pathPart : path) { - if (pathPart.startsWith("[") && pathPart.endsWith("]")) { - String index = pathPart.substring(1, pathPart.length()-1); - try { - current = Iterables.get((Iterable<?>)current, Integer.parseInt(index)); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Invalid index '"+index+"', in path "+path); - } catch (IndexOutOfBoundsException e) { - throw new IllegalArgumentException("Invalid index '"+index+"', in path "+path); - } - } else { - current = ((Map<String, ?>)current).get(pathPart); - } - if (current == null) return null; - } - return current; - } - - @SuppressWarnings("rawtypes") - public static void dump(int depth, Object r) { - if (r instanceof Iterable) { - for (Object ri : ((Iterable)r)) - dump(depth+1, ri); - } else if (r instanceof Map) { - for (Object re: ((Map)r).entrySet()) { - for (int i=0; i<depth; i++) System.out.print(" "); - System.out.println(((Entry)re).getKey()+":"); - dump(depth+1, ((Entry)re).getValue()); - } - } else { - for (int i=0; i<depth; i++) System.out.print(" "); - if (r==null) System.out.println("<null>"); - else System.out.println("<"+r.getClass().getSimpleName()+">"+" "+r); - } - } - - /** simplifies new Yaml().loadAll, and converts to list to prevent single-use iterable bug in yaml */ - @SuppressWarnings("unchecked") - public static Iterable<Object> parseAll(String yaml) { - Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml); - return (List<Object>) getAs(result, List.class); - } - - /** as {@link #parseAll(String)} */ - @SuppressWarnings("unchecked") - public static Iterable<Object> parseAll(Reader yaml) { - Iterable<Object> result = new org.yaml.snakeyaml.Yaml().loadAll(yaml); - return (List<Object>) getAs(result, List.class); - } - - public static Object removeMultinameAttribute(Map<String,Object> obj, String ...equivalentNames) { - Object result = null; - for (String name: equivalentNames) { - Object candidate = obj.remove(name); - if (candidate!=null) { - if (result==null) result = candidate; - else if (!result.equals(candidate)) { - log.warn("Different values for attributes "+Arrays.toString(equivalentNames)+"; " + - "preferring '"+result+"' to '"+candidate+"'"); - } - } - } - return result; - } - - public static Object getMultinameAttribute(Map<String,Object> obj, String ...equivalentNames) { - Object result = null; - for (String name: equivalentNames) { - Object candidate = obj.get(name); - if (candidate!=null) { - if (result==null) result = candidate; - else if (!result.equals(candidate)) { - log.warn("Different values for attributes "+Arrays.toString(equivalentNames)+"; " + - "preferring '"+result+"' to '"+candidate+"'"); - } - } - } - return result; - } - - @Beta - public static class YamlExtract { - String yaml; - NodeTuple focusTuple; - Node prev, key, focus, next; - Exception error; - boolean includeKey = false, includePrecedingComments = true, includeOriginalIndentation = false; - - private int indexStart(Node node, boolean defaultIsYamlEnd) { - if (node==null) return defaultIsYamlEnd ? yaml.length() : 0; - return index(node.getStartMark()); - } - private int indexEnd(Node node, boolean defaultIsYamlEnd) { - if (!found() || node==null) return defaultIsYamlEnd ? yaml.length() : 0; - return index(node.getEndMark()); - } - private int index(Mark mark) { - try { - return mark.getIndex(); - } catch (NoSuchMethodError e) { - try { - getClass().getClassLoader().loadClass("org.testng.TestNG"); - } catch (ClassNotFoundException e1) { - // not using TestNG - Exceptions.propagateIfFatal(e1); - throw e; - } - if (!LOGGED_TESTNG_WARNING.getAndSet(true)) { - log.warn("Detected TestNG/SnakeYAML version incompatibilities: " - + "some YAML source reconstruction will be unavailable. " - + "This can happen with TestNG plugins which force an older version of SnakeYAML " - + "which does not support Mark.getIndex. " - + "It should not occur from maven CLI runs. " - + "(Subsequent occurrences will be silently dropped, and source code reconstructed from YAML.)"); - } - // using TestNG - throw new KnownClassVersionException(e); - } - } - - static AtomicBoolean LOGGED_TESTNG_WARNING = new AtomicBoolean(); - static class KnownClassVersionException extends IllegalStateException { - private static final long serialVersionUID = -1620847775786753301L; - public KnownClassVersionException(Throwable e) { - super("Class version error. This can happen if using a TestNG plugin in your IDE " - + "which is an older version, dragging in an older version of SnakeYAML which does not support Mark.getIndex.", e); - } - } - - public int getEndOfPrevious() { - return indexEnd(prev, false); - } - @Nullable public Node getKey() { - return key; - } - public Node getResult() { - return focus; - } - public int getStartOfThis() { - if (includeKey && focusTuple!=null) return indexStart(focusTuple.getKeyNode(), false); - return indexStart(focus, false); - } - private int getStartColumnOfThis() { - if (includeKey && focusTuple!=null) return focusTuple.getKeyNode().getStartMark().getColumn(); - return focus.getStartMark().getColumn(); - } - public int getEndOfThis() { - return getEndOfThis(false); - } - private int getEndOfThis(boolean goToEndIfNoNext) { - if (next==null && goToEndIfNoNext) return yaml.length(); - return indexEnd(focus, false); - } - public int getStartOfNext() { - return indexStart(next, true); - } - - private static int initialWhitespaceLength(String x) { - int i=0; - while (i < x.length() && x.charAt(i)==' ') i++; - return i; - } - - public String getFullYamlTextOriginal() { - return yaml; - } - - /** Returns the original YAML with the found item replaced by the given replacement YAML. - * @param replacement YAML to put in for the found item; - * this YAML typically should not have any special indentation -- if required when replacing it will be inserted. - * <p> - * if replacing an inline map entry, the supplied entry must follow the structure being replaced; - * for example, if replacing the value in <code>key: value</code> with a map, - * supplying a replacement <code>subkey: value</code> would result in invalid yaml; - * the replacement must be supplied with a newline, either before the subkey or after. - * (if unsure we believe it is always valid to include an initial newline or comment with newline.) - */ - public String getFullYamlTextWithExtractReplaced(String replacement) { - if (!found()) throw new IllegalStateException("Cannot perform replacement when item was not matched."); - String result = yaml.substring(0, getStartOfThis()); - - String[] newLines = replacement.split("\n"); - for (int i=1; i<newLines.length; i++) - newLines[i] = Strings.makePaddedString("", getStartColumnOfThis(), "", " ") + newLines[i]; - result += Strings.lines(newLines); - if (replacement.endsWith("\n")) result += "\n"; - - int end = getEndOfThis(); - result += yaml.substring(end); - - return result; - } - - /** Specifies whether the key should be included in the found text, - * when calling {@link #getMatchedYamlText()} or {@link #getFullYamlTextWithExtractReplaced(String)}, - * if the found item is a map entry. - * Defaults to false. - * @return this object, for use in a fluent constructions - */ - public YamlExtract withKeyIncluded(boolean includeKey) { - this.includeKey = includeKey; - return this; - } - - /** Specifies whether comments preceding the found item should be included, - * when calling {@link #getMatchedYamlText()} or {@link #getFullYamlTextWithExtractReplaced(String)}. - * This will not include comments which are indented further than the item, - * as those will typically be aligned with the previous item - * (whereas comments whose indentation is the same or less than the found item - * will typically be aligned with this item). - * Defaults to true. - * @return this object, for use in a fluent constructions - */ - public YamlExtract withPrecedingCommentsIncluded(boolean includePrecedingComments) { - this.includePrecedingComments = includePrecedingComments; - return this; - } - - /** Specifies whether the original indentation should be preserved - * (and in the case of the first line, whether whitespace should be inserted so its start column is preserved), - * when calling {@link #getMatchedYamlText()}. - * Defaults to false, the returned text will be outdented as far as possible. - * @return this object, for use in a fluent constructions - */ - public YamlExtract withOriginalIndentation(boolean includeOriginalIndentation) { - this.includeOriginalIndentation = includeOriginalIndentation; - return this; - } - - @Beta - public String getMatchedYamlTextOrWarn() { - try { - return getMatchedYamlText(); - } catch (Exception e) { - Exceptions.propagateIfFatal(e); - if (e instanceof KnownClassVersionException) { - log.debug("Known class version exception; no yaml text being matched for "+this+": "+e); - } else { - if (e instanceof UserFacingException) { - log.warn("Unable to match yaml text in "+this+": "+e.getMessage()); - } else { - log.warn("Unable to match yaml text in "+this+": "+e, e); - } - } - return null; - } - } - - @Beta - public String getMatchedYamlText() { - if (!found()) return null; - - String[] body = yaml.substring(getStartOfThis(), getEndOfThis(true)).split("\n", -1); - - int firstLineIndentationOfFirstThing; - if (focusTuple!=null) { - firstLineIndentationOfFirstThing = focusTuple.getKeyNode().getStartMark().getColumn(); - } else { - firstLineIndentationOfFirstThing = focus.getStartMark().getColumn(); - } - int firstLineIndentationToAdd; - if (focusTuple!=null && (includeKey || body.length==1)) { - firstLineIndentationToAdd = focusTuple.getKeyNode().getStartMark().getColumn(); - } else { - firstLineIndentationToAdd = focus.getStartMark().getColumn(); - } - - - String firstLineIndentationToAddS = Strings.makePaddedString("", firstLineIndentationToAdd, "", " "); - String subsequentLineIndentationToRemoveS = firstLineIndentationToAddS; - -/* complexities of indentation: - -x: a - bc - -should become - -a - bc - -whereas - -- a: 0 - b: 1 - -selecting 0 should give - -a: 0 -b: 1 - - */ - List<String> result = MutableList.of(); - if (includePrecedingComments) { - if (getEndOfPrevious() > getStartOfThis()) { - throw new UserFacingException("YAML not in expected format; when scanning, previous end "+getEndOfPrevious()+" exceeds this start "+getStartOfThis()); - } - String[] preceding = yaml.substring(getEndOfPrevious(), getStartOfThis()).split("\n"); - // suppress comments which are on the same line as the previous item or indented more than firstLineIndentation, - // ensuring appropriate whitespace is added to preceding[0] if it starts mid-line - if (preceding.length>0 && prev!=null) { - preceding[0] = Strings.makePaddedString("", prev.getEndMark().getColumn(), "", " ") + preceding[0]; - } - for (String p: preceding) { - int w = initialWhitespaceLength(p); - p = p.substring(w); - if (p.startsWith("#")) { - // only add if the hash is not indented further than the first line - if (w <= firstLineIndentationOfFirstThing) { - if (includeOriginalIndentation) p = firstLineIndentationToAddS + p; - result.add(p); - } - } - } - } - - boolean doneFirst = false; - for (String p: body) { - if (!doneFirst) { - if (includeOriginalIndentation) { - // have to insert the right amount of spacing - p = firstLineIndentationToAddS + p; - } - result.add(p); - doneFirst = true; - } else { - if (includeOriginalIndentation) { - result.add(p); - } else { - result.add(Strings.removeFromStart(p, subsequentLineIndentationToRemoveS)); - } - } - } - return Strings.join(result, "\n"); - } - - boolean found() { - return focus != null; - } - - public Exception getError() { - return error; - } - - @Override - public String toString() { - return "Extract["+focus+";prev="+prev+";key="+key+";next="+next+"]"; - } - } - - private static void findTextOfYamlAtPath(YamlExtract result, int pathIndex, Object ...path) { - if (pathIndex>=path.length) { - // we're done - return; - } - - Object pathItem = path[pathIndex]; - Node node = result.focus; - - if (node.getNodeId()==NodeId.mapping && pathItem instanceof String) { - // find key - Iterator<NodeTuple> ti = ((MappingNode)node).getValue().iterator(); - while (ti.hasNext()) { - NodeTuple t = ti.next(); - Node key = t.getKeyNode(); - if (key.getNodeId()==NodeId.scalar) { - if (pathItem.equals( ((ScalarNode)key).getValue() )) { - result.key = key; - result.focus = t.getValueNode(); - if (pathIndex+1<path.length) { - // there are more path items, so the key here is a previous node - result.prev = key; - } else { - result.focusTuple = t; - } - findTextOfYamlAtPath(result, pathIndex+1, path); - if (result.next==null) { - if (ti.hasNext()) result.next = ti.next().getKeyNode(); - } - return; - } else { - result.prev = t.getValueNode(); - } - } else { - throw new IllegalStateException("Key "+key+" is not a scalar, searching for "+pathItem+" at depth "+pathIndex+" of "+Arrays.asList(path)); - } - } - throw new IllegalStateException("Did not find "+pathItem+" in "+node+" at depth "+pathIndex+" of "+Arrays.asList(path)); - - } else if (node.getNodeId()==NodeId.sequence && pathItem instanceof Number) { - // find list item - List<Node> nl = ((SequenceNode)node).getValue(); - int i = ((Number)pathItem).intValue(); - if (i>=nl.size()) - throw new IllegalStateException("Index "+i+" is out of bounds in "+node+", searching for "+pathItem+" at depth "+pathIndex+" of "+Arrays.asList(path)); - if (i>0) result.prev = nl.get(i-1); - result.key = null; - result.focus = nl.get(i); - findTextOfYamlAtPath(result, pathIndex+1, path); - if (result.next==null) { - if (nl.size()>i+1) result.next = nl.get(i+1); - } - return; - - } else { - throw new IllegalStateException("Node "+node+" does not match selector "+pathItem+" at depth "+pathIndex+" of "+Arrays.asList(path)); - } - - // unreachable - } - - - /** Given a path, where each segment consists of a string (key) or number (element in list), - * this will find the YAML text for that element - * <p> - * If not found this will return a {@link YamlExtract} - * where {@link YamlExtract#isMatch()} is false and {@link YamlExtract#getError()} is set. */ - public static YamlExtract getTextOfYamlAtPath(String yaml, Object ...path) { - YamlExtract result = new YamlExtract(); - if (yaml==null) return result; - try { - int pathIndex = 0; - result.yaml = yaml; - result.focus = new Yaml().compose(new StringReader(yaml)); - - findTextOfYamlAtPath(result, pathIndex, path); - return result; - } catch (NoSuchMethodError e) { - throw new IllegalStateException("Class version error. This can happen if using a TestNG plugin in your IDE " - + "which is an older version, dragging in an older version of SnakeYAML which does not support Mark.getIndex.", e); - } catch (Exception e) { - Exceptions.propagateIfFatal(e); - log.debug("Unable to find element in yaml (setting in result): "+e); - result.error = e; - return result; - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/cf2f7a93/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java ---------------------------------------------------------------------- diff --git a/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java new file mode 100644 index 0000000..4d581eb --- /dev/null +++ b/utils/common/src/main/java/org/apache/brooklyn/config/ConfigInheritance.java @@ -0,0 +1,50 @@ +/* + * 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.brooklyn.config; + +import java.io.Serializable; + +import com.google.common.annotations.Beta; + +@SuppressWarnings("serial") +public abstract class ConfigInheritance implements Serializable { + + public static final ConfigInheritance ALWAYS = new Always(); + public static final ConfigInheritance NONE = new None(); + + private ConfigInheritance() {} + + @Beta + public abstract boolean isInherited(ConfigKey<?> key, Object from, Object to); + + private static class Always extends ConfigInheritance { + @Override + public boolean isInherited(ConfigKey<?> key, Object from, Object to) { + return true; + } + } + + private static class None extends ConfigInheritance { + @Override + public boolean isInherited(ConfigKey<?> key, Object from, Object to) { + return false; + } + } + +}
