Skip to content

Commit 74e4068

Browse files
committed
Merge branch '2.x' into 3.x
2 parents c10c975 + cf227b3 commit 74e4068

5 files changed

Lines changed: 84 additions & 19 deletions

File tree

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ NOTE: Jackson 3.x components rely on 2.x annotations; there are no separate
1616

1717
2.20 (not yet released)
1818

19+
#293: Improve duplicate Id with different associated object error message
20+
(requested by @moutyque)
1921
#294: Drop patch number from version for 2.20 and later (no more 2.20.0)
2022
#296: Drop Java 6 compatibility for 2.20 (Java 8 baseline)
2123

src/main/java/com/fasterxml/jackson/annotation/JsonFormat.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* or String (such as ISO-8601 compatible time value) -- as well as configuring
1818
* exact details with {@link #pattern} property.
1919
*<p>
20-
* As of Jackson 3.0, known special handling includes:
20+
* As of Jackson 2.20, known special handling includes:
2121
*<ul>
2222
* <li>{@link java.util.Date} or {@link java.util.Calendar}: Shape can be {@link Shape#STRING} or {@link Shape#NUMBER};
2323
* pattern may contain {@link java.text.SimpleDateFormat}-compatible pattern definition.
@@ -149,6 +149,8 @@ public enum Shape
149149
/**
150150
* Value that indicates that Binary type (native, if format supports it;
151151
* encoding using Base64 if only textual types supported) should be used.
152+
*
153+
* @since 2.10
152154
*/
153155
BINARY,
154156

@@ -183,7 +185,7 @@ public enum Shape
183185

184186
/**
185187
* Value that indicates shape should not be structural (that is, not
186-
* {@link #ARRAY} or {@link #OBJECT}, but can be any other shape.
188+
* {@link #ARRAY} or {@link #OBJECT}), but can be any other shape.
187189
*/
188190
SCALAR,
189191

@@ -232,7 +234,7 @@ public boolean isNumeric() {
232234
return (this == NUMBER) || (this == NUMBER_INT) || (this == NUMBER_FLOAT);
233235
}
234236

235-
/** @since 3.0 */
237+
/** @since 2.20 */
236238
public static boolean isNumeric(Shape shapeOrNull) {
237239
return (shapeOrNull != null) && shapeOrNull.isNumeric();
238240
}
@@ -241,7 +243,7 @@ public boolean isStructured() {
241243
return (this == OBJECT) || (this == ARRAY) || (this == POJO);
242244
}
243245

244-
/** @since 3.0 */
246+
/** @since 2.20 */
245247
public static boolean isStructured(Shape shapeOrNull) {
246248
return (shapeOrNull != null) && shapeOrNull.isStructured();
247249
}
@@ -531,6 +533,9 @@ public Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f
531533
_lenient = lenient;
532534
}
533535

536+
/**
537+
* @since 2.7
538+
*/
534539
public final static Value empty() {
535540
return EMPTY;
536541
}

src/main/java/com/fasterxml/jackson/annotation/ObjectIdGenerator.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.fasterxml.jackson.annotation;
22

3+
import java.util.Objects;
4+
35
/**
46
* Definition of API used for constructing Object Identifiers
57
* (as annotated using {@link JsonIdentityInfo}).
@@ -144,18 +146,13 @@ public final static class IdKey
144146
private final int hashCode;
145147

146148
public IdKey(Class<?> type, Class<?> scope, Object key) {
147-
if (key == null) {
148-
throw new IllegalArgumentException("Can not construct IdKey for null key");
149-
}
150-
this.type = type;
149+
this.type = Objects.requireNonNull(type, "Type must not be null");
150+
// Scope can be null
151151
this.scope = scope;
152-
this.key = key;
152+
this.key = Objects.requireNonNull(key, "Key must not be null");
153153

154-
int h = key.hashCode() + type.getName().hashCode();
155-
if (scope != null) {
156-
h ^= scope.getName().hashCode();
157-
}
158-
hashCode = h;
154+
hashCode = Objects.hashCode(key) + Objects.hashCode(type.getName())
155+
^ Objects.hashCode(scope);
159156
}
160157

161158
@Override
@@ -174,7 +171,7 @@ public boolean equals(Object o)
174171
@Override
175172
public String toString() {
176173
return String.format("[ObjectId: key=%s, type=%s, scope=%s]", key,
177-
(type == null) ? "NONE" : type.getName(),
174+
type.getName(),
178175
(scope == null) ? "NONE" : scope.getName());
179176
}
180177
}

src/main/java/com/fasterxml/jackson/annotation/SimpleObjectIdResolver.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,46 @@
1111
* @author Pascal Gélinas
1212
*/
1313
public class SimpleObjectIdResolver implements ObjectIdResolver {
14-
protected Map<IdKey,Object> _items;
14+
protected Map<IdKey, Object> _items;
1515

1616
public SimpleObjectIdResolver() { }
1717

1818
@Override
1919
public void bindItem(IdKey id, Object ob)
2020
{
2121
if (_items == null) {
22-
_items = new HashMap<ObjectIdGenerator.IdKey,Object>();
22+
_items = new HashMap<>();
2323
} else {
2424
Object old = _items.get(id);
2525
if (old != null) {
2626
// 11-Nov-2020, tatu: As per [annotations#180] allow duplicate calls:
2727
if (old == ob) {
2828
return;
2929
}
30-
throw new IllegalStateException("Already had POJO for id (" + id.key.getClass().getName() + ") [" + id
31-
+ "]");
30+
throw new IllegalStateException(String.format(
31+
"Object Id conflict: Id %s already bound to an Object %s: attempt to re-bind to a different Object %s",
32+
id.toString(), _desc(old), _desc(ob)));
3233
}
3334
}
3435
_items.put(id, ob);
3536
}
3637

38+
private String _desc(Object ob) {
39+
if (ob == null) {
40+
return "(null)";
41+
}
42+
String desc;
43+
if (ob instanceof String) {
44+
desc = "\""+ob+"\"";
45+
} else {
46+
desc = ob.toString();
47+
if (desc.length() > 100) {
48+
desc = desc.substring(0, 100) + "[... truncated]";
49+
}
50+
}
51+
return ("(type: `"+ob.getClass().getName()+"`, value: "+desc+")");
52+
}
53+
3754
@Override
3855
public Object resolveId(IdKey id) {
3956
return (_items == null) ? null : _items.get(id);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.fasterxml.jackson.annotation;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import static org.junit.jupiter.api.Assertions.assertEquals;
6+
import static org.junit.jupiter.api.Assertions.fail;
7+
8+
public class SimpleObjectIdResolverTest
9+
{
10+
@Test
11+
public void testSimpleHandling()
12+
{
13+
// Let's create a simple ObjectIdResolver with 2 non-duplicate items
14+
SimpleObjectIdResolver resolver = new SimpleObjectIdResolver();
15+
ObjectIdGenerator.IdKey key1 = new ObjectIdGenerator.IdKey(String.class, null, "key1");
16+
ObjectIdGenerator.IdKey key2 = new ObjectIdGenerator.IdKey(String.class, null, "key2");
17+
18+
// Bind 2 non-duplicate items, check they can be resolved
19+
resolver.bindItem(key1, "value1");
20+
resolver.bindItem(key2, "value2");
21+
22+
assertEquals("value1", resolver.resolveId(key1));
23+
assertEquals("value2", resolver.resolveId(key2));
24+
assertEquals(2, resolver._items.size());
25+
26+
// And then verify that multiple bindings of the same key/bound value is ok
27+
resolver.bindItem(key1, "value1");
28+
resolver.bindItem(key2, "value2");
29+
assertEquals(2, resolver._items.size());
30+
31+
// But that overriding is not
32+
try {
33+
resolver.bindItem(key1, "value3");
34+
fail("Should have thrown an exception for re-binding");
35+
} catch (IllegalStateException e) {
36+
assertEquals(
37+
"Object Id conflict: Id [ObjectId: key=key1, type=java.lang.String, scope=NONE]"
38+
+" already bound to an Object (type: `java.lang.String`, value: \"value1\"):"
39+
+" attempt to re-bind to a different Object (type: `java.lang.String`, value: \"value3\")",
40+
e.getMessage());
41+
}
42+
}
43+
44+
}

0 commit comments

Comments
 (0)