From 98835fb8f8a8f144f2a1eac0b833ff83f4a05f54 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Mon, 19 Sep 2016 13:45:07 -0700 Subject: Integrate internal changes --- .../java/com/google/protobuf/util/Durations.java | 182 +++++++++++------- .../java/com/google/protobuf/util/JsonFormat.java | 15 +- .../java/com/google/protobuf/util/Timestamps.java | 207 +++++++++++++-------- .../com/google/protobuf/util/JsonFormatTest.java | 110 ++++++++++- .../com/google/protobuf/util/TimeUtilTest.java | 7 +- .../proto/com/google/protobuf/util/json_test.proto | 1 + 6 files changed, 363 insertions(+), 159 deletions(-) (limited to 'java/util') diff --git a/java/util/src/main/java/com/google/protobuf/util/Durations.java b/java/util/src/main/java/com/google/protobuf/util/Durations.java index 5fe6ebca..9333168d 100644 --- a/java/util/src/main/java/com/google/protobuf/util/Durations.java +++ b/java/util/src/main/java/com/google/protobuf/util/Durations.java @@ -30,50 +30,86 @@ package com.google.protobuf.util; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.math.IntMath.checkedAdd; +import static com.google.common.math.IntMath.checkedSubtract; +import static com.google.common.math.LongMath.checkedAdd; +import static com.google.common.math.LongMath.checkedMultiply; +import static com.google.common.math.LongMath.checkedSubtract; import static com.google.protobuf.util.Timestamps.MICROS_PER_SECOND; import static com.google.protobuf.util.Timestamps.MILLIS_PER_SECOND; import static com.google.protobuf.util.Timestamps.NANOS_PER_MICROSECOND; import static com.google.protobuf.util.Timestamps.NANOS_PER_MILLISECOND; import static com.google.protobuf.util.Timestamps.NANOS_PER_SECOND; +import com.google.common.collect.ComparisonChain; import com.google.protobuf.Duration; - import java.text.ParseException; +import java.util.Comparator; /** - * Utilities to help create/manipulate {@code protobuf/duration.proto}. + * Utilities to help create/manipulate {@code protobuf/duration.proto}. All operations throw an + * {@link IllegalArgumentException} if the input(s) are not {@linkplain #isValid(Duration) valid}. */ public final class Durations { static final long DURATION_SECONDS_MIN = -315576000000L; static final long DURATION_SECONDS_MAX = 315576000000L; - // TODO(kak): Do we want to expose Duration constants for MAX/MIN? + /** A constant holding the minimum valid {@link Duration}, approximately {@code -10,000} years. */ + public static final Duration MIN_VALUE = + Duration.newBuilder().setSeconds(DURATION_SECONDS_MIN).setNanos(-999999999).build(); + + /** A constant holding the maximum valid {@link Duration}, approximately {@code +10,000} years. */ + public static final Duration MAX_VALUE = + Duration.newBuilder().setSeconds(DURATION_SECONDS_MAX).setNanos(999999999).build(); private Durations() {} + private static final Comparator COMPARATOR = + new Comparator() { + @Override + public int compare(Duration d1, Duration d2) { + checkValid(d1); + checkValid(d2); + + return ComparisonChain.start() + .compare(d1.getSeconds(), d2.getSeconds()) + .compare(d1.getNanos(), d2.getNanos()) + .result(); + } + }; + + /** + * Returns a {@link Comparator} for {@link Duration}s which sorts in increasing chronological + * order. Nulls and invalid {@link Duration}s are not allowed (see {@link #isValid}). + */ + public static Comparator comparator() { + return COMPARATOR; + } + /** * Returns true if the given {@link Duration} is valid. The {@code seconds} value must be in the * range [-315,576,000,000, +315,576,000,000]. The {@code nanos} value must be in the range * [-999,999,999, +999,999,999]. * - *

Note: Durations less than one second are represented with a 0 {@code seconds} field and a - * positive or negative {@code nanos} field. For durations of one second or more, a non-zero value - * for the {@code nanos} field must be of the same sign as the {@code seconds} field. + *

Note: Durations less than one second are represented with a 0 {@code seconds} field + * and a positive or negative {@code nanos} field. For durations of one second or more, a non-zero + * value for the {@code nanos} field must be of the same sign as the {@code seconds} field. */ public static boolean isValid(Duration duration) { return isValid(duration.getSeconds(), duration.getNanos()); } /** - * Returns true if the given number of seconds and nanos is a valid {@link Duration}. The - * {@code seconds} value must be in the range [-315,576,000,000, +315,576,000,000]. The - * {@code nanos} value must be in the range [-999,999,999, +999,999,999]. + * Returns true if the given number of seconds and nanos is a valid {@link Duration}. The {@code + * seconds} value must be in the range [-315,576,000,000, +315,576,000,000]. The {@code nanos} + * value must be in the range [-999,999,999, +999,999,999]. * - *

Note: Durations less than one second are represented with a 0 {@code seconds} field and a - * positive or negative {@code nanos} field. For durations of one second or more, a non-zero value - * for the {@code nanos} field must be of the same sign as the {@code seconds} field. + *

Note: Durations less than one second are represented with a 0 {@code seconds} field + * and a positive or negative {@code nanos} field. For durations of one second or more, a non-zero + * value for the {@code nanos} field must be of the same sign as the {@code seconds} field. */ - public static boolean isValid(long seconds, long nanos) { + public static boolean isValid(long seconds, int nanos) { if (seconds < DURATION_SECONDS_MIN || seconds > DURATION_SECONDS_MAX) { return false; } @@ -88,35 +124,35 @@ public final class Durations { return true; } - /** - * Throws an {@link IllegalArgumentException} if the given seconds/nanos are not - * a valid {@link Duration}. - */ - private static void checkValid(long seconds, int nanos) { - if (!isValid(seconds, nanos)) { - throw new IllegalArgumentException(String.format( - "Duration is not valid. See proto definition for valid values. " - + "Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]." - + "Nanos (%s) must be in range [-999,999,999, +999,999,999]. " - + "Nanos must have the same sign as seconds", seconds, nanos)); - } + /** Throws an {@link IllegalArgumentException} if the given {@link Duration} is not valid. */ + public static Duration checkValid(Duration duration) { + long seconds = duration.getSeconds(); + int nanos = duration.getNanos(); + checkArgument( + isValid(seconds, nanos), + "Duration is not valid. See proto definition for valid values. " + + "Seconds (%s) must be in range [-315,576,000,000, +315,576,000,000]. " + + "Nanos (%s) must be in range [-999,999,999, +999,999,999]. " + + "Nanos must have the same sign as seconds", + seconds, + nanos); + return duration; } /** - * Convert Duration to string format. The string format will contains 3, 6, - * or 9 fractional digits depending on the precision required to represent - * the exact Duration value. For example: "1s", "1.010s", "1.000000100s", - * "-3.100s" The range that can be represented by Duration is from + * Convert Duration to string format. The string format will contains 3, 6, or 9 fractional digits + * depending on the precision required to represent the exact Duration value. For example: "1s", + * "1.010s", "1.000000100s", "-3.100s" The range that can be represented by Duration is from * -315,576,000,000 to +315,576,000,000 inclusive (in seconds). * * @return The string representation of the given duration. - * @throws IllegalArgumentException if the given duration is not in the valid - * range. + * @throws IllegalArgumentException if the given duration is not in the valid range. */ public static String toString(Duration duration) { + checkValid(duration); + long seconds = duration.getSeconds(); int nanos = duration.getNanos(); - checkValid(seconds, nanos); StringBuilder result = new StringBuilder(); if (seconds < 0 || nanos < 0) { @@ -172,9 +208,20 @@ public final class Durations { } } + /** Create a Duration from the number of seconds. */ + public static Duration fromSeconds(long seconds) { + return normalizedDuration(seconds, 0); + } + /** - * Create a Duration from the number of milliseconds. + * Convert a Duration to the number of seconds. The result will be rounded towards 0 to the + * nearest second. E.g., if the duration represents -1 nanosecond, it will be rounded to 0. */ + public static long toSeconds(Duration duration) { + return checkValid(duration).getSeconds(); + } + + /** Create a Duration from the number of milliseconds. */ public static Duration fromMillis(long milliseconds) { return normalizedDuration( milliseconds / MILLIS_PER_SECOND, @@ -182,17 +229,17 @@ public final class Durations { } /** - * Convert a Duration to the number of milliseconds.The result will be - * rounded towards 0 to the nearest millisecond. E.g., if the duration - * represents -1 nanosecond, it will be rounded to 0. + * Convert a Duration to the number of milliseconds. The result will be rounded towards 0 to the + * nearest millisecond. E.g., if the duration represents -1 nanosecond, it will be rounded to 0. */ public static long toMillis(Duration duration) { - return duration.getSeconds() * MILLIS_PER_SECOND + duration.getNanos() / NANOS_PER_MILLISECOND; + checkValid(duration); + return checkedAdd( + checkedMultiply(duration.getSeconds(), MILLIS_PER_SECOND), + duration.getNanos() / NANOS_PER_MILLISECOND); } - /** - * Create a Duration from the number of microseconds. - */ + /** Create a Duration from the number of microseconds. */ public static Duration fromMicros(long microseconds) { return normalizedDuration( microseconds / MICROS_PER_SECOND, @@ -200,57 +247,60 @@ public final class Durations { } /** - * Convert a Duration to the number of microseconds.The result will be - * rounded towards 0 to the nearest microseconds. E.g., if the duration - * represents -1 nanosecond, it will be rounded to 0. + * Convert a Duration to the number of microseconds. The result will be rounded towards 0 to the + * nearest microseconds. E.g., if the duration represents -1 nanosecond, it will be rounded to 0. */ public static long toMicros(Duration duration) { - return duration.getSeconds() * MICROS_PER_SECOND + duration.getNanos() / NANOS_PER_MICROSECOND; + checkValid(duration); + return checkedAdd( + checkedMultiply(duration.getSeconds(), MICROS_PER_SECOND), + duration.getNanos() / NANOS_PER_MICROSECOND); } - /** - * Create a Duration from the number of nanoseconds. - */ + /** Create a Duration from the number of nanoseconds. */ public static Duration fromNanos(long nanoseconds) { return normalizedDuration( nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND)); } - /** - * Convert a Duration to the number of nanoseconds. - */ + /** Convert a Duration to the number of nanoseconds. */ public static long toNanos(Duration duration) { - return duration.getSeconds() * NANOS_PER_SECOND + duration.getNanos(); + checkValid(duration); + return checkedAdd( + checkedMultiply(duration.getSeconds(), NANOS_PER_SECOND), duration.getNanos()); } - /** - * Add two durations. - */ + /** Add two durations. */ public static Duration add(Duration d1, Duration d2) { - return normalizedDuration(d1.getSeconds() + d2.getSeconds(), d1.getNanos() + d2.getNanos()); + checkValid(d1); + checkValid(d2); + return normalizedDuration( + checkedAdd(d1.getSeconds(), d2.getSeconds()), checkedAdd(d1.getNanos(), d2.getNanos())); } - /** - * Subtract a duration from another. - */ + /** Subtract a duration from another. */ public static Duration subtract(Duration d1, Duration d2) { - return normalizedDuration(d1.getSeconds() - d2.getSeconds(), d1.getNanos() - d2.getNanos()); + checkValid(d1); + checkValid(d2); + return normalizedDuration( + checkedSubtract(d1.getSeconds(), d2.getSeconds()), + checkedSubtract(d1.getNanos(), d2.getNanos())); } static Duration normalizedDuration(long seconds, int nanos) { if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) { - seconds += nanos / NANOS_PER_SECOND; + seconds = checkedAdd(seconds, nanos / NANOS_PER_SECOND); nanos %= NANOS_PER_SECOND; } if (seconds > 0 && nanos < 0) { - nanos += NANOS_PER_SECOND; - seconds -= 1; + nanos += NANOS_PER_SECOND; // no overflow since nanos is negative (and we're adding) + seconds--; // no overflow since seconds is positive (and we're decrementing) } if (seconds < 0 && nanos > 0) { - nanos -= NANOS_PER_SECOND; - seconds += 1; + nanos -= NANOS_PER_SECOND; // no overflow since nanos is positive (and we're subtracting) + seconds++; // no overflow since seconds is negative (and we're incrementing) } - checkValid(seconds, nanos); - return Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build(); + Duration duration = Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build(); + return checkValid(duration); } } diff --git a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java index d4db9c80..6361b4ac 100644 --- a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java +++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java @@ -640,6 +640,10 @@ public class JsonFormat { /** Prints google.protobuf.Any */ private void printAny(MessageOrBuilder message) throws IOException { + if (Any.getDefaultInstance().equals(message)) { + generator.print("{}"); + return; + } Descriptor descriptor = message.getDescriptorForType(); FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url"); FieldDescriptor valueField = descriptor.findFieldByName("value"); @@ -1235,6 +1239,9 @@ public class JsonFormat { throw new InvalidProtocolBufferException("Expect message object but got: " + json); } JsonObject object = (JsonObject) json; + if (object.entrySet().isEmpty()) { + return; // builder never modified, so it will end up building the default instance of Any + } JsonElement typeUrlElement = object.get("@type"); if (typeUrlElement == null) { throw new InvalidProtocolBufferException("Missing type url when parsing: " + json); @@ -1327,6 +1334,9 @@ public class JsonFormat { Message.Builder listBuilder = builder.newBuilderForField(field); merge(json, listBuilder); builder.setField(field, listBuilder.build()); + } else if (json instanceof JsonNull) { + builder.setField( + type.findFieldByName("null_value"), NullValue.NULL_VALUE.getValueDescriptor()); } else { throw new IllegalStateException("Unexpected json data: " + json); } @@ -1620,11 +1630,6 @@ public class JsonFormat { } private ByteString parseBytes(JsonElement json) throws InvalidProtocolBufferException { - String encoded = json.getAsString(); - if (encoded.length() % 4 != 0) { - throw new InvalidProtocolBufferException( - "Bytes field is not encoded in standard BASE64 with paddings: " + encoded); - } return ByteString.copyFrom(BaseEncoding.base64().decode(json.getAsString())); } diff --git a/java/util/src/main/java/com/google/protobuf/util/Timestamps.java b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java index f1f202da..52b1ab98 100644 --- a/java/util/src/main/java/com/google/protobuf/util/Timestamps.java +++ b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java @@ -30,19 +30,29 @@ package com.google.protobuf.util; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.math.IntMath.checkedAdd; +import static com.google.common.math.IntMath.checkedSubtract; +import static com.google.common.math.LongMath.checkedAdd; +import static com.google.common.math.LongMath.checkedMultiply; +import static com.google.common.math.LongMath.checkedSubtract; + +import com.google.common.collect.ComparisonChain; import com.google.protobuf.Duration; import com.google.protobuf.Timestamp; - import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Comparator; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; /** - * Utilities to help create/manipulate {@code protobuf/timestamp.proto}. + * Utilities to help create/manipulate {@code protobuf/timestamp.proto}. All operations throw an + * {@link IllegalArgumentException} if the input(s) are not {@linkplain #isValid(Timestamp) valid}. */ public final class Timestamps { + // Timestamp for "0001-01-01T00:00:00Z" static final long TIMESTAMP_SECONDS_MIN = -62135596800L; @@ -55,10 +65,19 @@ public final class Timestamps { static final long MILLIS_PER_SECOND = 1000; static final long MICROS_PER_SECOND = 1000000; - // TODO(kak): Do we want to expose Timestamp constants for MAX/MIN? + /** A constant holding the minimum valid {@link Timestamp}, {@code 0001-01-01T00:00:00Z}. */ + public static final Timestamp MIN_VALUE = + Timestamp.newBuilder().setSeconds(TIMESTAMP_SECONDS_MIN).setNanos(0).build(); + + /** + * A constant holding the maximum valid {@link Timestamp}, {@code 9999-12-31T23:59:59.999999999Z}. + */ + public static final Timestamp MAX_VALUE = + Timestamp.newBuilder().setSeconds(TIMESTAMP_SECONDS_MAX).setNanos(999999999).build(); private static final ThreadLocal timestampFormat = new ThreadLocal() { + @Override protected SimpleDateFormat initialValue() { return createTimestampFormat(); } @@ -76,28 +95,50 @@ public final class Timestamps { private Timestamps() {} + private static final Comparator COMPARATOR = + new Comparator() { + @Override + public int compare(Timestamp t1, Timestamp t2) { + checkValid(t1); + checkValid(t2); + + return ComparisonChain.start() + .compare(t1.getSeconds(), t2.getSeconds()) + .compare(t1.getNanos(), t2.getNanos()) + .result(); + } + }; + + /** + * Returns a {@link Comparator} for {@link Timestamp}s which sorts in increasing chronological + * order. Nulls and invalid {@link Timestamp}s are not allowed (see {@link #isValid}). + */ + public static Comparator comparator() { + return COMPARATOR; + } + /** * Returns true if the given {@link Timestamp} is valid. The {@code seconds} value must be in the * range [-62,135,596,800, +253,402,300,799] (i.e., between 0001-01-01T00:00:00Z and * 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range [0, +999,999,999]. * - *

Note: Negative second values with fractions must still have non-negative nanos value that - * counts forward in time. + *

Note: Negative second values with fractional seconds must still have non-negative + * nanos values that count forward in time. */ public static boolean isValid(Timestamp timestamp) { return isValid(timestamp.getSeconds(), timestamp.getNanos()); } /** - * Returns true if the given number of seconds and nanos is a valid {@link Timestamp}. The - * {@code seconds} value must be in the range [-62,135,596,800, +253,402,300,799] (i.e., between + * Returns true if the given number of seconds and nanos is a valid {@link Timestamp}. The {@code + * seconds} value must be in the range [-62,135,596,800, +253,402,300,799] (i.e., between * 0001-01-01T00:00:00Z and 9999-12-31T23:59:59Z). The {@code nanos} value must be in the range * [0, +999,999,999]. * - *

Note: Negative second values with fractions must still have non-negative nanos value that - * counts forward in time. + *

Note: Negative second values with fractional seconds must still have non-negative + * nanos values that count forward in time. */ - public static boolean isValid(long seconds, long nanos) { + public static boolean isValid(long seconds, int nanos) { if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) { return false; } @@ -107,37 +148,37 @@ public final class Timestamps { return true; } - /** - * Throws an {@link IllegalArgumentException} if the given seconds/nanos are not - * a valid {@link Timestamp}. - */ - private static void checkValid(long seconds, int nanos) { - if (!isValid(seconds, nanos)) { - throw new IllegalArgumentException(String.format( - "Timestamp is not valid. See proto definition for valid values. " - + "Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]." - + "Nanos (%s) must be in range [0, +999,999,999].", - seconds, nanos)); - } + /** Throws an {@link IllegalArgumentException} if the given {@link Timestamp} is not valid. */ + public static Timestamp checkValid(Timestamp timestamp) { + long seconds = timestamp.getSeconds(); + int nanos = timestamp.getNanos(); + checkArgument( + isValid(seconds, nanos), + "Timestamp is not valid. See proto definition for valid values. " + + "Seconds (%s) must be in range [-62,135,596,800, +253,402,300,799]. " + + "Nanos (%s) must be in range [0, +999,999,999].", + seconds, + nanos); + return timestamp; } /** - * Convert Timestamp to RFC 3339 date string format. The output will always - * be Z-normalized and uses 3, 6 or 9 fractional digits as required to - * represent the exact value. Note that Timestamp can only represent time - * from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. See + * Convert Timestamp to RFC 3339 date string format. The output will always be Z-normalized and + * uses 3, 6 or 9 fractional digits as required to represent the exact value. Note that Timestamp + * can only represent time from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. See * https://www.ietf.org/rfc/rfc3339.txt * *

Example of generated format: "1972-01-01T10:00:20.021Z" * * @return The string representation of the given timestamp. - * @throws IllegalArgumentException if the given timestamp is not in the - * valid range. + * @throws IllegalArgumentException if the given timestamp is not in the valid range. */ public static String toString(Timestamp timestamp) { + checkValid(timestamp); + long seconds = timestamp.getSeconds(); int nanos = timestamp.getNanos(); - checkValid(seconds, nanos); + StringBuilder result = new StringBuilder(); // Format the seconds part. Date date = new Date(seconds * MILLIS_PER_SECOND); @@ -152,10 +193,9 @@ public final class Timestamps { } /** - * Parse from RFC 3339 date string to Timestamp. This method accepts all - * outputs of {@link #toString(Timestamp)} and it also accepts any fractional - * digits (or none) and any offset as long as they fit into nano-seconds - * precision. + * Parse from RFC 3339 date string to Timestamp. This method accepts all outputs of {@link + * #toString(Timestamp)} and it also accepts any fractional digits (or none) and any offset as + * long as they fit into nano-seconds precision. * *

Example of accepted format: "1972-01-01T10:00:20.021-05:00" * @@ -210,13 +250,26 @@ public final class Timestamps { try { return normalizedTimestamp(seconds, nanos); } catch (IllegalArgumentException e) { - throw new ParseException("Failed to parse timestmap: timestamp is out of range.", 0); + throw new ParseException("Failed to parse timestamp: timestamp is out of range.", 0); } } + /** Create a Timestamp from the number of seconds elapsed from the epoch. */ + public static Timestamp fromSeconds(long seconds) { + return normalizedTimestamp(seconds, 0); + } + /** - * Create a Timestamp from the number of milliseconds elapsed from the epoch. + * Convert a Timestamp to the number of seconds elapsed from the epoch. + * + *

The result will be rounded down to the nearest second. E.g., if the timestamp represents + * "1969-12-31T23:59:59.999999999Z", it will be rounded to -1 second. */ + public static long toSeconds(Timestamp timestamp) { + return checkValid(timestamp).getSeconds(); + } + + /** Create a Timestamp from the number of milliseconds elapsed from the epoch. */ public static Timestamp fromMillis(long milliseconds) { return normalizedTimestamp( milliseconds / MILLIS_PER_SECOND, @@ -226,18 +279,17 @@ public final class Timestamps { /** * Convert a Timestamp to the number of milliseconds elapsed from the epoch. * - *

The result will be rounded down to the nearest millisecond. E.g., if the - * timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded - * to -1 millisecond. + *

The result will be rounded down to the nearest millisecond. E.g., if the timestamp + * represents "1969-12-31T23:59:59.999999999Z", it will be rounded to -1 millisecond. */ public static long toMillis(Timestamp timestamp) { - return timestamp.getSeconds() * MILLIS_PER_SECOND - + timestamp.getNanos() / NANOS_PER_MILLISECOND; + checkValid(timestamp); + return checkedAdd( + checkedMultiply(timestamp.getSeconds(), MILLIS_PER_SECOND), + timestamp.getNanos() / NANOS_PER_MILLISECOND); } - /** - * Create a Timestamp from the number of microseconds elapsed from the epoch. - */ + /** Create a Timestamp from the number of microseconds elapsed from the epoch. */ public static Timestamp fromMicros(long microseconds) { return normalizedTimestamp( microseconds / MICROS_PER_SECOND, @@ -247,65 +299,67 @@ public final class Timestamps { /** * Convert a Timestamp to the number of microseconds elapsed from the epoch. * - *

The result will be rounded down to the nearest microsecond. E.g., if the - * timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded - * to -1 millisecond. + *

The result will be rounded down to the nearest microsecond. E.g., if the timestamp + * represents "1969-12-31T23:59:59.999999999Z", it will be rounded to -1 millisecond. */ public static long toMicros(Timestamp timestamp) { - return timestamp.getSeconds() * MICROS_PER_SECOND - + timestamp.getNanos() / NANOS_PER_MICROSECOND; + checkValid(timestamp); + return checkedAdd( + checkedMultiply(timestamp.getSeconds(), MICROS_PER_SECOND), + timestamp.getNanos() / NANOS_PER_MICROSECOND); } - /** - * Create a Timestamp from the number of nanoseconds elapsed from the epoch. - */ + /** Create a Timestamp from the number of nanoseconds elapsed from the epoch. */ public static Timestamp fromNanos(long nanoseconds) { return normalizedTimestamp( nanoseconds / NANOS_PER_SECOND, (int) (nanoseconds % NANOS_PER_SECOND)); } - /** - * Convert a Timestamp to the number of nanoseconds elapsed from the epoch. - */ + /** Convert a Timestamp to the number of nanoseconds elapsed from the epoch. */ public static long toNanos(Timestamp timestamp) { - return timestamp.getSeconds() * NANOS_PER_SECOND + timestamp.getNanos(); + checkValid(timestamp); + return checkedAdd( + checkedMultiply(timestamp.getSeconds(), NANOS_PER_SECOND), timestamp.getNanos()); } - /** - * Calculate the difference between two timestamps. - */ + /** Calculate the difference between two timestamps. */ public static Duration between(Timestamp from, Timestamp to) { + checkValid(from); + checkValid(to); return Durations.normalizedDuration( - to.getSeconds() - from.getSeconds(), to.getNanos() - from.getNanos()); + checkedSubtract(to.getSeconds(), from.getSeconds()), + checkedSubtract(to.getNanos(), from.getNanos())); } - /** - * Add a duration to a timestamp. - */ + /** Add a duration to a timestamp. */ public static Timestamp add(Timestamp start, Duration length) { + checkValid(start); + Durations.checkValid(length); return normalizedTimestamp( - start.getSeconds() + length.getSeconds(), start.getNanos() + length.getNanos()); + checkedAdd(start.getSeconds(), length.getSeconds()), + checkedAdd(start.getNanos(), length.getNanos())); } - /** - * Subtract a duration from a timestamp. - */ + /** Subtract a duration from a timestamp. */ public static Timestamp subtract(Timestamp start, Duration length) { + checkValid(start); + Durations.checkValid(length); return normalizedTimestamp( - start.getSeconds() - length.getSeconds(), start.getNanos() - length.getNanos()); + checkedSubtract(start.getSeconds(), length.getSeconds()), + checkedSubtract(start.getNanos(), length.getNanos())); } - private static Timestamp normalizedTimestamp(long seconds, int nanos) { + static Timestamp normalizedTimestamp(long seconds, int nanos) { if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) { - seconds += nanos / NANOS_PER_SECOND; + seconds = checkedAdd(seconds, nanos / NANOS_PER_SECOND); nanos %= NANOS_PER_SECOND; } if (nanos < 0) { - nanos += NANOS_PER_SECOND; - seconds -= 1; + nanos += NANOS_PER_SECOND; // no overflow since nanos is negative (and we're adding) + seconds = checkedSubtract(seconds, 1); } - checkValid(seconds, nanos); - return Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build(); + Timestamp timestamp = Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build(); + return checkValid(timestamp); } private static long parseTimezoneOffset(String value) throws ParseException { @@ -324,7 +378,7 @@ public final class Timestamps { result = result * 10; if (i < value.length()) { if (value.charAt(i) < '0' || value.charAt(i) > '9') { - throw new ParseException("Invalid nanosecnds.", 0); + throw new ParseException("Invalid nanoseconds.", 0); } result += value.charAt(i) - '0'; } @@ -332,11 +386,8 @@ public final class Timestamps { return result; } - /** - * Format the nano part of a timestamp or a duration. - */ + /** Format the nano part of a timestamp or a duration. */ static String formatNanos(int nanos) { - assert nanos >= 1 && nanos <= 999999999; // Determine whether to use 3, 6, or 9 digits for the nano part. if (nanos % NANOS_PER_MILLISECOND == 0) { return String.format("%1$03d", nanos / NANOS_PER_MILLISECOND); diff --git a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java index c11114c0..32739d44 100644 --- a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java +++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java @@ -60,12 +60,10 @@ import com.google.protobuf.util.JsonTestProto.TestOneof; import com.google.protobuf.util.JsonTestProto.TestStruct; import com.google.protobuf.util.JsonTestProto.TestTimestamp; import com.google.protobuf.util.JsonTestProto.TestWrappers; - -import junit.framework.TestCase; - import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; +import junit.framework.TestCase; public class JsonFormatTest extends TestCase { private void setAllFields(TestAllTypes.Builder builder) { @@ -819,6 +817,15 @@ public class JsonFormatTest extends TestCase { printer.print(message)); assertRoundTripEquals(message, registry); + TestAny messageWithDefaultAnyValue = + TestAny.newBuilder().setAnyValue(Any.getDefaultInstance()).build(); + assertEquals( + "{\n" + + " \"anyValue\": {}\n" + + "}", + printer.print(messageWithDefaultAnyValue)); + assertRoundTripEquals(messageWithDefaultAnyValue, registry); + // Well-known types have a special formatting when embedded in Any. // // 1. Any in Any. @@ -952,6 +959,8 @@ public class JsonFormatTest extends TestCase { + "}", printer.print(anyMessage)); assertRoundTripEquals(anyMessage, registry); + + // 7. Value (number type) in Any Value.Builder valueBuilder = Value.newBuilder(); valueBuilder.setNumberValue(1); anyMessage = Any.pack(valueBuilder.build()); @@ -962,6 +971,95 @@ public class JsonFormatTest extends TestCase { + "}", printer.print(anyMessage)); assertRoundTripEquals(anyMessage, registry); + + // 8. Value (null type) in Any + anyMessage = Any.pack(Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n" + + " \"value\": null\n" + + "}", + printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + } + + public void testAnyInMaps() throws Exception { + JsonFormat.TypeRegistry registry = + JsonFormat.TypeRegistry.newBuilder().add(TestAllTypes.getDescriptor()).build(); + JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry); + + TestAny.Builder testAny = TestAny.newBuilder(); + testAny.putAnyMap("int32_wrapper", Any.pack(Int32Value.newBuilder().setValue(123).build())); + testAny.putAnyMap("int64_wrapper", Any.pack(Int64Value.newBuilder().setValue(456).build())); + testAny.putAnyMap("timestamp", Any.pack(Timestamps.parse("1969-12-31T23:59:59Z"))); + testAny.putAnyMap("duration", Any.pack(Durations.parse("12345.1s"))); + testAny.putAnyMap("field_mask", Any.pack(FieldMaskUtil.fromString("foo.bar,baz"))); + Value numberValue = Value.newBuilder().setNumberValue(1.125).build(); + Struct.Builder struct = Struct.newBuilder(); + struct.putFields("number", numberValue); + testAny.putAnyMap("struct", Any.pack(struct.build())); + Value nullValue = Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build(); + testAny.putAnyMap( + "list_value", + Any.pack(ListValue.newBuilder().addValues(numberValue).addValues(nullValue).build())); + testAny.putAnyMap("number_value", Any.pack(numberValue)); + testAny.putAnyMap("any_value_number", Any.pack(Any.pack(numberValue))); + testAny.putAnyMap("any_value_default", Any.pack(Any.getDefaultInstance())); + testAny.putAnyMap("default", Any.getDefaultInstance()); + + assertEquals( + "{\n" + + " \"anyMap\": {\n" + + " \"int32_wrapper\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Int32Value\",\n" + + " \"value\": 123\n" + + " },\n" + + " \"int64_wrapper\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Int64Value\",\n" + + " \"value\": \"456\"\n" + + " },\n" + + " \"timestamp\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Timestamp\",\n" + + " \"value\": \"1969-12-31T23:59:59Z\"\n" + + " },\n" + + " \"duration\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n" + + " \"value\": \"12345.100s\"\n" + + " },\n" + + " \"field_mask\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.FieldMask\",\n" + + " \"value\": \"foo.bar,baz\"\n" + + " },\n" + + " \"struct\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Struct\",\n" + + " \"value\": {\n" + + " \"number\": 1.125\n" + + " }\n" + + " },\n" + + " \"list_value\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.ListValue\",\n" + + " \"value\": [1.125, null]\n" + + " },\n" + + " \"number_value\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n" + + " \"value\": 1.125\n" + + " },\n" + + " \"any_value_number\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Any\",\n" + + " \"value\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Value\",\n" + + " \"value\": 1.125\n" + + " }\n" + + " },\n" + + " \"any_value_default\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Any\",\n" + + " \"value\": {}\n" + + " },\n" + + " \"default\": {}\n" + + " }\n" + + "}", + printer.print(testAny.build())); + assertRoundTripEquals(testAny.build(), registry); } public void testParserMissingTypeUrl() throws Exception { @@ -1016,8 +1114,10 @@ public class JsonFormatTest extends TestCase { public void testParserRejectInvalidBase64() throws Exception { assertRejects("optionalBytes", "!@#$"); - // We use standard BASE64 with paddings. - assertRejects("optionalBytes", "AQI"); + } + + public void testParserAcceptBase64Variants() throws Exception { + assertAccepts("optionalBytes", "AQI"); } public void testParserRejectInvalidEnumValue() throws Exception { diff --git a/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java b/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java index a41528ec..5af83d88 100644 --- a/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java +++ b/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java @@ -32,14 +32,11 @@ package com.google.protobuf.util; import com.google.protobuf.Duration; import com.google.protobuf.Timestamp; - -import junit.framework.TestCase; - -import org.junit.Assert; - import java.text.ParseException; import java.util.ArrayList; import java.util.List; +import junit.framework.TestCase; +import org.junit.Assert; /** Unit tests for {@link TimeUtil}. */ public class TimeUtilTest extends TestCase { diff --git a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto index 4bf223f2..bd22f65a 100644 --- a/java/util/src/test/proto/com/google/protobuf/util/json_test.proto +++ b/java/util/src/test/proto/com/google/protobuf/util/json_test.proto @@ -195,6 +195,7 @@ message TestStruct { message TestAny { google.protobuf.Any any_value = 1; + map any_map = 2; } message TestCustomJsonName { -- cgit v1.2.3 From ebcda12102f2beaa630a81f5675854abb44746c1 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Tue, 20 Sep 2016 21:29:02 +0000 Subject: Bump version number to 3.1.0-alpha-1. --- Protobuf.podspec | 2 +- configure.ac | 2 +- csharp/Google.Protobuf.Tools.nuspec | 2 +- csharp/src/Google.Protobuf/project.json | 2 +- java/core/pom.xml | 2 +- java/lite/pom.xml | 2 +- java/pom.xml | 2 +- java/util/pom.xml | 2 +- javanano/pom.xml | 2 +- js/package.json | 2 +- php/ext/google/protobuf/package.xml | 2 +- protoc-artifacts/pom.xml | 2 +- python/google/protobuf/__init__.py | 2 +- ruby/Gemfile.lock | 2 +- ruby/google-protobuf.gemspec | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) (limited to 'java/util') diff --git a/Protobuf.podspec b/Protobuf.podspec index 8d1ab357..2aebdda4 100644 --- a/Protobuf.podspec +++ b/Protobuf.podspec @@ -5,7 +5,7 @@ # dependent projects use the :git notation to refer to the library. Pod::Spec.new do |s| s.name = 'Protobuf' - s.version = '3.0.2' + s.version = '3.1.0-alpha-1' s.summary = 'Protocol Buffers v.3 runtime library for Objective-C.' s.homepage = 'https://github.com/google/protobuf' s.license = 'New BSD' diff --git a/configure.ac b/configure.ac index 757f55c9..c21990b5 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ AC_PREREQ(2.59) # In the SVN trunk, the version should always be the next anticipated release # version with the "-pre" suffix. (We used to use "-SNAPSHOT" but this pushed # the size of one file name in the dist tarfile over the 99-char limit.) -AC_INIT([Protocol Buffers],[3.0.2],[protobuf@googlegroups.com],[protobuf]) +AC_INIT([Protocol Buffers],[3.1.0-alpha-1],[protobuf@googlegroups.com],[protobuf]) AM_MAINTAINER_MODE([enable]) diff --git a/csharp/Google.Protobuf.Tools.nuspec b/csharp/Google.Protobuf.Tools.nuspec index ce7514b4..79f32bab 100644 --- a/csharp/Google.Protobuf.Tools.nuspec +++ b/csharp/Google.Protobuf.Tools.nuspec @@ -5,7 +5,7 @@ Google Protocol Buffers tools

Tools for Protocol Buffers - Google's data interchange format. See project site for more info. - 3.0.2 + 3.1.0-alpha1 Google Inc. protobuf-packages https://github.com/google/protobuf/blob/master/LICENSE diff --git a/csharp/src/Google.Protobuf/project.json b/csharp/src/Google.Protobuf/project.json index 607b65ef..dd958c98 100644 --- a/csharp/src/Google.Protobuf/project.json +++ b/csharp/src/Google.Protobuf/project.json @@ -1,5 +1,5 @@ { - "version": "3.0.2", + "version": "3.1.0-alpha-1", "title": "Google Protocol Buffers", "description": "See project site for more info.", "authors": [ "Google Inc." ], diff --git a/java/core/pom.xml b/java/core/pom.xml index 28bc99a0..790f0af3 100644 --- a/java/core/pom.xml +++ b/java/core/pom.xml @@ -6,7 +6,7 @@ com.google.protobuf protobuf-parent - 3.0.2 + 3.1.0-alpha-1 protobuf-java diff --git a/java/lite/pom.xml b/java/lite/pom.xml index 9862cd94..d6490247 100644 --- a/java/lite/pom.xml +++ b/java/lite/pom.xml @@ -6,7 +6,7 @@ com.google.protobuf protobuf-parent - 3.0.0 + 3.1.0-alpha-1 protobuf-lite diff --git a/java/pom.xml b/java/pom.xml index ccfb229e..3fa0a85b 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -11,7 +11,7 @@ com.google.protobuf protobuf-parent - 3.0.2 + 3.1.0-alpha-1 pom Protocol Buffers [Parent] diff --git a/java/util/pom.xml b/java/util/pom.xml index 58cc4b30..4d6ad7fa 100644 --- a/java/util/pom.xml +++ b/java/util/pom.xml @@ -6,7 +6,7 @@ com.google.protobuf protobuf-parent - 3.0.2 + 3.1.0-alpha-1 protobuf-java-util diff --git a/javanano/pom.xml b/javanano/pom.xml index 6ebba3cf..291ed27a 100644 --- a/javanano/pom.xml +++ b/javanano/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf.nano protobuf-javanano - 3.0.0-alpha-7 + 3.1.0-alpha-1 bundle Protocol Buffer JavaNano API diff --git a/js/package.json b/js/package.json index 5df7283c..cc0db47b 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "google-protobuf", - "version": "3.0.2", + "version": "3.1.0-alpha.1", "description": "Protocol Buffers for JavaScript", "main": "google-protobuf.js", "files": [ diff --git a/php/ext/google/protobuf/package.xml b/php/ext/google/protobuf/package.xml index 83719091..cfb33acb 100644 --- a/php/ext/google/protobuf/package.xml +++ b/php/ext/google/protobuf/package.xml @@ -13,7 +13,7 @@ 2016-09-02 - 3.1.0 + 3.1.0a1 3.1.0 diff --git a/protoc-artifacts/pom.xml b/protoc-artifacts/pom.xml index c4f0a73d..b8581da1 100644 --- a/protoc-artifacts/pom.xml +++ b/protoc-artifacts/pom.xml @@ -10,7 +10,7 @@ com.google.protobuf protoc - 3.0.2 + 3.1.0-alpha-1 pom Protobuf Compiler diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py index 30ee6e81..f52137c4 100755 --- a/python/google/protobuf/__init__.py +++ b/python/google/protobuf/__init__.py @@ -30,7 +30,7 @@ # Copyright 2007 Google Inc. All Rights Reserved. -__version__ = '3.0.2' +__version__ = '3.1.0a1' if __name__ != '__main__': try: diff --git a/ruby/Gemfile.lock b/ruby/Gemfile.lock index d0eb9cc4..7a89c866 100644 --- a/ruby/Gemfile.lock +++ b/ruby/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - google-protobuf (3.0.0.alpha.5.0.5) + google-protobuf (3.1.0.alpha.1.0) GEM remote: https://rubygems.org/ diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 67b15c33..26d9b20d 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = "google-protobuf" - s.version = "3.0.2" + s.version = "3.1.0.alpha.1.0" s.licenses = ["BSD"] s.summary = "Protocol Buffers" s.description = "Protocol Buffers are Google's data interchange format." -- cgit v1.2.3 From 6cb6bd9f213a7610ff180dbac7226a22a9f71867 Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Tue, 20 Sep 2016 22:04:48 +0000 Subject: Fix gson dependency. gson 2.3 has internal bug that it doesn't work with some versions of maven. --- WORKSPACE | 2 +- java/util/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'java/util') diff --git a/WORKSPACE b/WORKSPACE index df919ad1..2a49e372 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -44,7 +44,7 @@ bind( maven_jar( name = "gson_maven", - artifact = "com.google.code.gson:gson:2.3", + artifact = "com.google.code.gson:gson:2.7", ) bind( diff --git a/java/util/pom.xml b/java/util/pom.xml index 4d6ad7fa..56a4e528 100644 --- a/java/util/pom.xml +++ b/java/util/pom.xml @@ -28,7 +28,7 @@ com.google.code.gson gson - 2.3 + 2.7 junit -- cgit v1.2.3 From 8c88762e3ef78b81435ecfba973c2e9d3b74f13b Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Thu, 22 Sep 2016 19:24:19 -0700 Subject: Update version number. --- java/util/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'java/util') diff --git a/java/util/pom.xml b/java/util/pom.xml index 56a4e528..6b07bcdf 100644 --- a/java/util/pom.xml +++ b/java/util/pom.xml @@ -6,7 +6,7 @@ com.google.protobuf protobuf-parent - 3.1.0-alpha-1 + 3.1.0 protobuf-java-util -- cgit v1.2.3 From eb7f3a3ad1e89ab5de0846c354bbc682e63bd837 Mon Sep 17 00:00:00 2001 From: Christopher Tubbs Date: Fri, 4 Nov 2016 02:11:55 -0400 Subject: Use latest maven-compiler-plugin (2.6.0) * Uses build-helper-maven-plugin to add generated sources to the classpath * Fixes an issue building with newer versions of the maven-compiler-plugin (See https://issues.apache.org/jira/browse/MCOMPILER-240) --- java/compatibility_tests/v2.5.0/protos/pom.xml | 2 +- java/core/pom.xml | 33 ++++++++++++++++++++++---- java/lite/pom.xml | 32 +++++++++++++++++++++++-- java/pom.xml | 2 +- java/util/pom.xml | 22 +++++++++++++---- 5 files changed, 77 insertions(+), 14 deletions(-) (limited to 'java/util') diff --git a/java/compatibility_tests/v2.5.0/protos/pom.xml b/java/compatibility_tests/v2.5.0/protos/pom.xml index 24447bdc..a22e91ed 100644 --- a/java/compatibility_tests/v2.5.0/protos/pom.xml +++ b/java/compatibility_tests/v2.5.0/protos/pom.xml @@ -28,7 +28,7 @@ maven-compiler-plugin - 3.3 + 3.6.0 1.6 1.6 diff --git a/java/core/pom.xml b/java/core/pom.xml index 8a83eb4e..cced344e 100644 --- a/java/core/pom.xml +++ b/java/core/pom.xml @@ -92,11 +92,34 @@ - maven-compiler-plugin - - ${generated.sources.dir} - ${generated.testsources.dir} - + org.codehaus.mojo + build-helper-maven-plugin + + + add-generated-sources + generate-sources + + add-source + + + + ${generated.sources.dir} + + + + + add-generated-test-sources + generate-test-sources + + add-test-source + + + + ${generated.testsources.dir} + + + + diff --git a/java/lite/pom.xml b/java/lite/pom.xml index 9862cd94..d7b15097 100644 --- a/java/lite/pom.xml +++ b/java/lite/pom.xml @@ -75,11 +75,39 @@ + + org.codehaus.mojo + build-helper-maven-plugin + + + add-generated-sources + generate-sources + + add-source + + + + ${generated.sources.lite.dir} + + + + + add-generated-test-sources + generate-test-sources + + add-test-source + + + + ${generated.testsources.lite.dir} + + + + + maven-compiler-plugin - ${generated.sources.lite.dir} - ${generated.testsources.lite.dir} **/AbstractMessageLite.java **/AbstractParser.java diff --git a/java/pom.xml b/java/pom.xml index 881473f3..6789e7c1 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -94,7 +94,7 @@ maven-compiler-plugin - 3.3 + 3.6.0 1.6 1.6 diff --git a/java/util/pom.xml b/java/util/pom.xml index 6b07bcdf..0ccfc848 100644 --- a/java/util/pom.xml +++ b/java/util/pom.xml @@ -79,12 +79,24 @@ + - maven-compiler-plugin - - - ${generated.testsources.dir} - + org.codehaus.mojo + build-helper-maven-plugin + + + add-generated-test-sources + generate-test-sources + + add-test-source + + + + ${generated.testsources.dir} + + + + -- cgit v1.2.3