Skip to content

Commit 5c48390

Browse files
committed
Merge branch 'master' into ng-3.0
2 parents 9d11c90 + 424f56c commit 5c48390

File tree

8 files changed

+122
-28
lines changed

8 files changed

+122
-28
lines changed

release-notes/CREDITS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,10 @@ Bertrand Renuart (brenuart@github)
632632
(2.8.9)
633633
* Reported #1651: `StdDateFormat` fails to parse 'zulu' date when TimeZone other than UTC
634634
(2.8.9)
635+
* Suggested #1745: StdDateFormat: accept and truncate millis larger than 3 digits
636+
(2.9.1)
637+
* Contributed #1749: StdDateFormat: performance improvement of '_format(..)' method
638+
(2.9.1)
635639

636640
Kevin Gallardo (newkek@github)
637641
* Reported #1658: Infinite recursion when deserializing a class extending a Map,
@@ -693,3 +697,8 @@ Christian Basler (Dissem@github)
693697
* Reported #1688: Deserialization fails for `java.nio.file.Path` implementations when
694698
default typing enabled
695699
(2.9.0)
700+
701+
Tim Bartley (tbartley@github)
702+
* Reported, suggested fix for #1705: Non-generic interface method hides type resolution info
703+
from generic base class
704+
(2.9.1)

release-notes/VERSION

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,16 @@ Project: jackson-databind
55

66
2.9.1 (not yet released)
77

8+
#1705: Non-generic interface method hides type resolution info from generic base class
9+
(reported by Tim B)
810
#1725: `NPE` In `TypeFactory. constructParametricType(...)`
911
(reported by ctytgat@github)
12+
#1730: InvalidFormatException` for `JsonToken.VALUE_EMBEDDED_OBJECT`
13+
(reported by zigzago@github)
14+
#1745: StdDateFormat: accept and truncate millis larger than 3 digits
15+
(suggested by Bertrand R)
16+
#1749: StdDateFormat: performance improvement of '_format(..)' method
17+
(contributed by Bertrand R)
1018
- Fix `DelegatingDeserializer` constructor to pass `handledType()` (and
1119
not type of deserializer being delegated to!)
1220

@@ -110,7 +118,7 @@ Project: jackson-databind
110118
`MapperFeature.ALLOW_COERCION_OF_SCALARS`
111119
(requested by magdel@github)
112120

113-
2.8.10 (not yet released)
121+
2.8.10 (24-Aug-2017)
114122

115123
#1657: `StdDateFormat` deserializes dates with no tz/offset as UTC instead of
116124
configured timezone

src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1458,7 +1458,7 @@ public Object deserializeFromEmbedded(JsonParser p, DeserializationContext ctxt)
14581458
// more meaningful exceptions.
14591459
Object value = p.getEmbeddedObject();
14601460
if (value != null) {
1461-
if (!_beanType.getClass().isInstance(value)) {
1461+
if (!_beanType.isTypeOrSuperTypeOf(value.getClass())) {
14621462
// allow this to be handled...
14631463
value = ctxt.handleWeirdNativeValue(_beanType, value, p);
14641464
}

src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodCollector.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ private void _addMemberMethods(TypeResolutionContext tc,
131131
// may also result in faster method calls (interface calls are slightly
132132
// costlier than regular method calls)
133133
b.method = m;
134+
// 23-Aug-2017, tatu: [databind#1705] Also need to change the type resolution context if so
135+
// (note: mix-over case above shouldn't need it)
136+
// b.typeContext = tc;
134137
}
135138
}
136139
}
@@ -176,7 +179,7 @@ private boolean _isIncludableMemberMethod(Method m)
176179
}
177180

178181
private final static class MethodBuilder {
179-
public final TypeResolutionContext typeContext;
182+
public TypeResolutionContext typeContext;
180183

181184
// Method left empty for "floating" mix-in, filled in as need be
182185
public Method method;

src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,21 @@ public class StdDateFormat
101101
DATE_FORMAT_ISO8601 = new SimpleDateFormat(DATE_FORMAT_STR_ISO8601, DEFAULT_LOCALE);
102102
DATE_FORMAT_ISO8601.setTimeZone(DEFAULT_TIMEZONE);
103103
}
104-
104+
105105
/**
106106
* A singleton instance can be used for cloning purposes, as a blueprint of sorts.
107107
*/
108108
public final static StdDateFormat instance = new StdDateFormat();
109-
109+
110+
/**
111+
* Blueprint "Calendar" instance for use during formatting. Cannot be used as is,
112+
* due to thread-safety issues, but can be used for constructing actual instances
113+
* more cheaply by cloning.
114+
*
115+
* @since 2.9.1
116+
*/
117+
protected static final Calendar CALENDAR = new GregorianCalendar(DEFAULT_TIMEZONE, DEFAULT_LOCALE);
118+
110119
/**
111120
* Caller may want to explicitly override timezone to use; if so,
112121
* we will have non-null value here.
@@ -124,6 +133,13 @@ public class StdDateFormat
124133
* @since 2.7
125134
*/
126135
protected Boolean _lenient;
136+
137+
/**
138+
* Lazily instantiated calendar used by this instance for serialization ({@link #format(Date)}).
139+
*
140+
* @since 2.9.1
141+
*/
142+
private transient Calendar _calendar;
127143

128144
private transient DateFormat _formatRFC1123;
129145

@@ -313,28 +329,35 @@ public StringBuffer format(Date date, StringBuffer toAppendTo,
313329
_format(tz, _locale, date, toAppendTo);
314330
return toAppendTo;
315331
}
316-
317-
protected static void _format(TimeZone tz, Locale loc, Date date,
332+
333+
protected void _format(TimeZone tz, Locale loc, Date date,
318334
StringBuffer buffer)
319335
{
320-
Calendar calendar = new GregorianCalendar(tz, loc);
321-
calendar.setTime(date);
336+
Calendar cal = _calendar;
337+
if (cal == null ) {
338+
_calendar = cal = (Calendar)CALENDAR.clone();
339+
}
340+
if (!cal.getTimeZone().equals(tz) ) {
341+
cal.setTimeZone(tz);
342+
}
343+
// Note: Calendar locale not updated since we don't need it here...
344+
cal.setTime(date);
322345

323-
pad4(buffer, calendar.get(Calendar.YEAR));
346+
pad4(buffer, cal.get(Calendar.YEAR));
324347
buffer.append('-');
325-
pad2(buffer, calendar.get(Calendar.MONTH) + 1);
348+
pad2(buffer, cal.get(Calendar.MONTH) + 1);
326349
buffer.append('-');
327-
pad2(buffer, calendar.get(Calendar.DAY_OF_MONTH));
350+
pad2(buffer, cal.get(Calendar.DAY_OF_MONTH));
328351
buffer.append('T');
329-
pad2(buffer, calendar.get(Calendar.HOUR_OF_DAY));
352+
pad2(buffer, cal.get(Calendar.HOUR_OF_DAY));
330353
buffer.append(':');
331-
pad2(buffer, calendar.get(Calendar.MINUTE));
354+
pad2(buffer, cal.get(Calendar.MINUTE));
332355
buffer.append(':');
333-
pad2(buffer, calendar.get(Calendar.SECOND));
356+
pad2(buffer, cal.get(Calendar.SECOND));
334357
buffer.append('.');
335-
pad3(buffer, calendar.get(Calendar.MILLISECOND));
358+
pad3(buffer, cal.get(Calendar.MILLISECOND));
336359

337-
int offset = tz.getOffset(calendar.getTimeInMillis());
360+
int offset = tz.getOffset(cal.getTimeInMillis());
338361
if (offset != 0) {
339362
int hours = Math.abs((offset / (60 * 1000)) / 60);
340363
int minutes = Math.abs((offset / (60 * 1000)) % 60);
@@ -498,8 +521,7 @@ protected Date _parseAsISO8601(String dateStr, ParsePosition pos)
498521
} else {
499522
Matcher m = PATTERN_ISO8601.matcher(dateStr);
500523
if (m.matches()) {
501-
// Important! START with optional timezone; otherwise
502-
// Calendar will implode.
524+
// Important! START with optional time zone; otherwise Calendar will explode
503525

504526
int start = m.start(2);
505527
int end = m.end(2);
@@ -546,20 +568,27 @@ protected Date _parseAsISO8601(String dateStr, ParsePosition pos)
546568
} else {
547569
// first char is '.', but rest....
548570
msecs = 0;
549-
switch (end-start) {
571+
final int fractLen = end-start;
572+
switch (fractLen) {
573+
default: // [databind#1745] Allow longer fractions... for now, cap at nanoseconds tho
574+
575+
if (fractLen > 9) { // only allow up to nanos
576+
throw new ParseException(String.format(
577+
"Cannot parse date \"%s\": invalid fractional seconds '%s'; can use at most 9 digits",
578+
dateStr, m.group(1).substring(1)
579+
),
580+
pos.getErrorIndex());
581+
}
582+
// fall through
550583
case 3:
551584
msecs += (dateStr.charAt(start+2) - '0');
552585
case 2:
553586
msecs += 10 * (dateStr.charAt(start+1) - '0');
554587
case 1:
555588
msecs += 100 * (dateStr.charAt(start) - '0');
556589
break;
557-
default:
558-
throw new ParseException(String.format(
559-
"Cannot parse date \"%s\": invalid fractional seconds '%s'; can use at most 3 digits",
560-
dateStr, m.group(1).substring(1)
561-
),
562-
pos.getErrorIndex());
590+
case 0:
591+
break;
563592
}
564593
cal.set(Calendar.MILLISECOND, msecs);
565594
}

src/test/java/com/fasterxml/jackson/databind/deser/jdk/DateDeserializationTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,31 @@ public void testISO8601PartialMilliseconds() throws Exception
223223
assertEquals(450, c.get(Calendar.MILLISECOND));
224224
}
225225

226+
// [Databind#1745]
227+
public void testISO8601FractSecondsLong() throws Exception
228+
{
229+
String inputStr;
230+
Date inputDate;
231+
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
232+
233+
inputStr = "2014-10-03T18:00:00.3456-05:00";
234+
inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
235+
c.setTime(inputDate);
236+
assertEquals(2014, c.get(Calendar.YEAR));
237+
assertEquals(Calendar.OCTOBER, c.get(Calendar.MONTH));
238+
assertEquals(3, c.get(Calendar.DAY_OF_MONTH));
239+
// should truncate; not error or round
240+
assertEquals(345, c.get(Calendar.MILLISECOND));
241+
242+
// But! Still limit to 9 digits (nanoseconds)
243+
try {
244+
MAPPER.readValue(quote("2014-10-03T18:00:00.1234567890-05:00"), java.util.Date.class);
245+
} catch (InvalidFormatException e) {
246+
verifyException(e, "invalid fractional seconds");
247+
verifyException(e, "can use at most 9 digits");
248+
}
249+
}
250+
226251
public void testISO8601MissingSeconds() throws Exception
227252
{
228253
String inputStr;

src/test/java/com/fasterxml/jackson/databind/introspect/TestPOJOPropertiesCollector.java renamed to src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import com.fasterxml.jackson.databind.*;
1212
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
1313

14-
public class TestPOJOPropertiesCollector
14+
public class POJOPropertiesCollectorTest
1515
extends BaseMapTest
1616
{
1717
static class Simple {

src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ public class TestTokenBuffer extends BaseMapTest
1515
{
1616
private final ObjectMapper MAPPER = objectMapper();
1717

18+
static class Base1730 { }
19+
20+
static class Sub1730 extends Base1730 { }
21+
1822
/*
1923
/**********************************************************
2024
/* Basic TokenBuffer tests
@@ -573,7 +577,7 @@ public void testWithMultipleJsonParserSequences() throws IOException
573577
buf2.close();
574578
buf3.close();
575579
buf4.close();
576-
}
580+
}
577581

578582
// [databind#743]
579583
public void testRawValues() throws Exception
@@ -592,4 +596,20 @@ public void testRawValues() throws Exception
592596
// then verify it would be serialized just fine
593597
assertEquals(RAW, MAPPER.writeValueAsString(buf));
594598
}
599+
600+
// [databind#1730]
601+
public void testEmbeddedObjectCoerceCheck() throws Exception
602+
{
603+
TokenBuffer buf = new TokenBuffer(null, false);
604+
Object inputPojo = new Sub1730();
605+
buf.writeEmbeddedObject(inputPojo);
606+
607+
// first: raw value won't be transformed in any way:
608+
JsonParser p = buf.asParser();
609+
Base1730 out = MAPPER.readValue(p, Base1730.class);
610+
611+
assertSame(inputPojo, out);
612+
p.close();
613+
buf.close();
614+
}
595615
}

0 commit comments

Comments
 (0)