Skip to content

Commit 798ba3d

Browse files
committed
Add DateTime, add Void deserializer
1 parent 74164d8 commit 798ba3d

File tree

11 files changed

+730
-6
lines changed

11 files changed

+730
-6
lines changed

java-client/build.gradle.kts

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ java {
3737
withSourcesJar()
3838
}
3939

40-
tasks.withType<ProcessResources> {
40+
tasks.getByName<ProcessResources>("processResources") {
41+
// Only process main source-set resources (test files are large)
4142
expand(
4243
"version" to version,
4344
"git_revision" to (if (rootProject.extra.has("gitHashFull")) rootProject.extra["gitHashFull"] else "unknown")

java-client/src/main/java/co/elastic/clients/json/JsonpDeserializerBase.java

+15
Original file line numberDiff line numberDiff line change
@@ -372,4 +372,19 @@ public Map<K, V> deserialize(JsonParser parser, JsonpMapper mapper, Event event)
372372
return result;
373373
}
374374
}
375+
376+
static class VoidDeserializer extends JsonpDeserializerBase<Void> {
377+
378+
public static final VoidDeserializer INSTANCE = new VoidDeserializer();
379+
380+
VoidDeserializer() {
381+
super(EnumSet.allOf(Event.class));
382+
}
383+
384+
@Override
385+
public Void deserialize(JsonParser parser, JsonpMapper mapper, Event event) {
386+
JsonpUtils.skipValue(parser, event);
387+
return null;
388+
}
389+
}
375390
}

java-client/src/main/java/co/elastic/clients/json/JsonpMapperBase.java

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public static <T> JsonpDeserializer<T> findDeserializer(Class<T> clazz) {
5454
}
5555
}
5656

57+
if (clazz == Void.class) {
58+
return (JsonpDeserializer<T>)JsonpDeserializerBase.VoidDeserializer.INSTANCE;
59+
}
60+
5761
return null;
5862
}
5963

java-client/src/main/java/co/elastic/clients/json/jackson/JacksonJsonpLocation.java

+5
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,9 @@ public long getStreamOffset() {
5151
long charOffset = location.getCharOffset();
5252
return charOffset == -1 ? location.getByteOffset() : charOffset;
5353
}
54+
55+
@Override
56+
public String toString() {
57+
return "(line no="+location.getLineNr()+", column no="+location.getColumnNr()+", offset="+ location.getCharOffset() +")";
58+
}
5459
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package co.elastic.clients.util;
21+
22+
import co.elastic.clients.json.JsonpDeserializable;
23+
import co.elastic.clients.json.JsonpDeserializer;
24+
import co.elastic.clients.json.JsonpDeserializerBase;
25+
import co.elastic.clients.json.JsonpMapper;
26+
import co.elastic.clients.json.JsonpSerializable;
27+
import jakarta.json.stream.JsonGenerator;
28+
import jakarta.json.stream.JsonParser;
29+
30+
import javax.annotation.Nullable;
31+
import java.time.Instant;
32+
import java.time.ZoneOffset;
33+
import java.time.ZonedDateTime;
34+
import java.time.format.DateTimeFormatter;
35+
import java.time.format.DateTimeParseException;
36+
import java.time.temporal.ChronoField;
37+
import java.time.temporal.Temporal;
38+
import java.util.EnumSet;
39+
import java.util.Locale;
40+
41+
/**
42+
* A date-time that can be represented either as a string or a number of milliseconds since the Epoch.
43+
* <p>
44+
* The string for can also be used to represent date-math expressions where applicable.
45+
*/
46+
@JsonpDeserializable
47+
public class DateTime implements JsonpSerializable {
48+
49+
private final long millis;
50+
@Nullable
51+
private final String str;
52+
53+
// Visible for testing
54+
@Nullable
55+
final DateTimeFormatter formatter;
56+
57+
private DateTime(long epochMillis, String str, DateTimeFormatter format) {
58+
this.millis = epochMillis;
59+
this.str = str;
60+
this.formatter = format;
61+
}
62+
63+
/**
64+
* Create a new date-time from Epoch milliseconds. It will be serialized to JSON as a string.
65+
*
66+
* @param epochMilli the timestamp
67+
* @param format the format used to format the date-time as a JSON string
68+
*/
69+
public static DateTime ofEpochMilli(long epochMilli, DateTimeFormatter format) {
70+
return new DateTime(epochMilli, null, format);
71+
}
72+
73+
/**
74+
* Create a new date-time from Epoch milliseconds. It will be serialized to JSON as a number.
75+
*
76+
* @param epochMilli the timestamp
77+
*/
78+
public static DateTime ofEpochMilli(long epochMilli) {
79+
return ofEpochMilli(epochMilli, null);
80+
}
81+
82+
/**
83+
* Create a new date-time or a date-math expression from a string. It will be serialized to JSON as is,
84+
* conversion to an <code>Instant</code> will use the default parsers.
85+
*
86+
* @param text the date-time as a string, or a date-math expression. If a date-math expression,
87+
* the result will fail to be converted to another type.
88+
*/
89+
public static DateTime of(String text) {
90+
return of(text, null);
91+
}
92+
93+
/**
94+
* Create a new date-time from a string and parser. It will be serialized to JSON as is.
95+
*
96+
* @param text the date-time as a string
97+
* @param parser the parser used to convert the date-time to an <code>Instant</code>
98+
*/
99+
public static DateTime of(String text, DateTimeFormatter parser) {
100+
return new DateTime(0, text, parser);
101+
}
102+
103+
/**
104+
* Create a new date-time from a <code>Temporal</code> object. It will be serialized to JSON as milliseconds since the Epoch.
105+
*
106+
* @param instant the date-time
107+
*/
108+
public static DateTime of(Temporal instant) {
109+
return of(instant, null);
110+
}
111+
112+
/**
113+
* Create a new date-time from a <code>Temporal</code> object. It will be serialized to JSON as a string.
114+
*
115+
* @param instant the date-time
116+
* @param format the format used to convert to a JSON string
117+
*/
118+
public static DateTime of(Temporal instant, DateTimeFormatter format) {
119+
long millis = instant.getLong(ChronoField.INSTANT_SECONDS)*1000L + instant.getLong(ChronoField.MILLI_OF_SECOND);
120+
return new DateTime(millis, null, format);
121+
}
122+
123+
/**
124+
* Get the string that was used to create this date-time, or <code>null</code>
125+
* if it was created with a number of milliseconds.
126+
*/
127+
@Nullable
128+
public String getString() {
129+
return str;
130+
}
131+
132+
/**
133+
* Get the formatter associated with this date-time, if any.
134+
*/
135+
@Nullable
136+
DateTimeFormatter getFormatter() {
137+
return formatter;
138+
}
139+
140+
/**
141+
* Converts this object to an <code>Instant</code>.
142+
*
143+
* @throws DateTimeParseException if this object was created from a string which can not be parsed
144+
*/
145+
public Instant toInstant() {
146+
if (str == null) {
147+
return Instant.ofEpochMilli(millis);
148+
} else {
149+
if (formatter == null) {
150+
try {
151+
ZonedDateTime zdt = DateTimeUtil.from(
152+
DateTimeUtil.STRICT_DATE_OPTIONAL_TIME_FORMATTER.parse(str),
153+
Locale.ROOT,
154+
ZoneOffset.UTC
155+
);
156+
return zdt.toInstant();
157+
} catch(DateTimeParseException dtpe) {
158+
// Ignore
159+
}
160+
161+
// Try milliseconds as string
162+
try {
163+
long ms = Long.parseLong(str);
164+
return Instant.ofEpochMilli(ms);
165+
} catch(NumberFormatException nfe) {
166+
throw new DateTimeParseException(
167+
"Cannot parse date-time with format [strict_date_optional_time||epoch_millis]", str, 0
168+
);
169+
}
170+
} else {
171+
return formatter.parse(str, Instant::from);
172+
}
173+
}
174+
}
175+
176+
/**
177+
* Converts this object to an <code>Instant</code>, using a given format.
178+
*
179+
* @param parser the parser to use if this object was created from a string
180+
* @throws DateTimeParseException if this object was created from a string which can not be parsed
181+
*/
182+
public Instant toInstant(DateTimeFormatter parser) {
183+
if (str == null) {
184+
return Instant.ofEpochMilli(millis);
185+
} else {
186+
return parser.parse(str, Instant::from);
187+
}
188+
}
189+
190+
/**
191+
* Converts this object to an <code>ZonedDateTime</code>.
192+
*
193+
* @throws DateTimeParseException if this object was created from a string which can not be parsed
194+
*/
195+
public ZonedDateTime toZonedDateTime() {
196+
if (str == null) {
197+
return ZonedDateTime.ofInstant(toInstant(), ZoneOffset.UTC);
198+
} else {
199+
try {
200+
return DateTimeUtil.from(
201+
DateTimeUtil.STRICT_DATE_OPTIONAL_TIME_FORMATTER.parse(str),
202+
Locale.ROOT,
203+
ZoneOffset.UTC
204+
);
205+
} catch(DateTimeParseException dtpe) {
206+
// Ignore
207+
}
208+
209+
// Try milliseconds as string
210+
try {
211+
long ms = Long.parseLong(str);
212+
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(ms), ZoneOffset.UTC);
213+
} catch(NumberFormatException nfe) {
214+
throw new DateTimeParseException("Cannot parse date-time with format [strict_date_optional_time||epoch_millis]", str, 0);
215+
}
216+
}
217+
}
218+
219+
/**
220+
* Converts this object to an <code>ZonedDateTime</code>, using a given format.
221+
*
222+
* @param parser the parser to use if this object was created from a string
223+
* @throws DateTimeParseException if this object was created from a string which can not be parsed
224+
*/
225+
public ZonedDateTime toZonedDateTime(DateTimeFormatter parser) {
226+
if (str == null) {
227+
return ZonedDateTime.ofInstant(toInstant(), ZoneOffset.UTC);
228+
} else {
229+
return parser.parse(str, ZonedDateTime::from);
230+
}
231+
}
232+
233+
/**
234+
* Get the milliseconds since the Epoch for represented by this object. If it was created from a string,
235+
* it is first converted using {@link #toInstant()}.
236+
*
237+
* @throws DateTimeParseException if this object was created from a string which can not be parsed
238+
*/
239+
public long toEpochMilli() {
240+
if (str == null) {
241+
return millis;
242+
} else {
243+
return toInstant().toEpochMilli();
244+
}
245+
}
246+
247+
/**
248+
* Get the milliseconds since the Epoch for represented by this object. If it was created from a string,
249+
* it is first converted using {@link #toInstant(DateTimeFormatter)}.
250+
*
251+
* @param format the parser to use if this object was created from a string
252+
* @throws DateTimeParseException if this object was created from a string which can not be parsed
253+
*/
254+
public long toEpochMilli(DateTimeFormatter format) {
255+
if (str == null) {
256+
return millis;
257+
} else {
258+
return toInstant(format).toEpochMilli();
259+
}
260+
}
261+
262+
@Override
263+
public void serialize(JsonGenerator generator, JsonpMapper mapper) {
264+
if (str == null) {
265+
if (formatter == null) {
266+
generator.write(millis);
267+
} else {
268+
generator.write(formatter.format(Instant.ofEpochMilli(millis).atZone(ZoneOffset.UTC)));
269+
}
270+
} else {
271+
generator.write(str);
272+
}
273+
}
274+
275+
@Override
276+
public boolean equals(Object o) {
277+
if (this == o) return true;
278+
if (o == null || getClass() != o.getClass()) return false;
279+
return this.toInstant().equals(((DateTime) o).toInstant());
280+
}
281+
282+
@Override
283+
public int hashCode() {
284+
return toInstant().hashCode();
285+
}
286+
287+
@Override
288+
public String toString() {
289+
if (str == null) {
290+
if (formatter == null) {
291+
return String.valueOf(millis);
292+
} else {
293+
return formatter.format(Instant.ofEpochMilli(millis).atZone(ZoneOffset.UTC));
294+
}
295+
} else {
296+
return str;
297+
}
298+
}
299+
300+
public static JsonpDeserializer<DateTime> _DESERIALIZER = new TimestampDeserializer();
301+
302+
private static class TimestampDeserializer extends JsonpDeserializerBase<DateTime> {
303+
TimestampDeserializer() {
304+
super(EnumSet.of(JsonParser.Event.VALUE_NUMBER, JsonParser.Event.VALUE_STRING));
305+
}
306+
307+
@Override
308+
public DateTime deserialize(JsonParser parser, JsonpMapper mapper, JsonParser.Event event) {
309+
if (event == JsonParser.Event.VALUE_NUMBER) {
310+
return DateTime.ofEpochMilli(parser.getLong());
311+
} else {
312+
return DateTime.of(parser.getString());
313+
}
314+
}
315+
}
316+
}

0 commit comments

Comments
 (0)