From 122d6af7385ddb90d1239844742142faa85a31ab Mon Sep 17 00:00:00 2001
From: David Kral <david.k.kral@oracle.com>
Date: Tue, 25 May 2021 15:18:12 +0200
Subject: [PATCH] Transient and property annotation merging

Signed-off-by: David Kral <david.k.kral@oracle.com>
---
 .../eclipse/yasson/internal/ClassParser.java  | 20 +++--
 .../basic/PropertyMismatchTest.java           | 75 +++++++++++++++++--
 2 files changed, 82 insertions(+), 13 deletions(-)

diff --git a/src/main/java/org/eclipse/yasson/internal/ClassParser.java b/src/main/java/org/eclipse/yasson/internal/ClassParser.java
index 0c377206f..63ee6e7a4 100644
--- a/src/main/java/org/eclipse/yasson/internal/ClassParser.java
+++ b/src/main/java/org/eclipse/yasson/internal/ClassParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0 which is available at
@@ -100,14 +100,22 @@ void parseProperties(ClassModel classModel, JsonbAnnotatedElement<Class<?>> clas
     }
 
     private static void mergePropertyModels(List<PropertyModel> unsortedMerged) {
-        PropertyModel[] clone = unsortedMerged.toArray(new PropertyModel[unsortedMerged.size()]);
+        PropertyModel[] clone = unsortedMerged.toArray(new PropertyModel[0]);
         for (int i = 0; i < clone.length; i++) {
             for (int j = i + 1; j < clone.length; j++) {
-                if (clone[i].equals(clone[j])) {
+                PropertyModel firstPropertyModel = clone[i];
+                PropertyModel secondPropertyModel = clone[j];
+                if (firstPropertyModel.equals(secondPropertyModel)) {
                     // Need to merge two properties
-                    unsortedMerged.remove(clone[i]);
-                    unsortedMerged.remove(clone[j]);
-                    unsortedMerged.add(new PropertyModel(clone[i], clone[j]));
+                    unsortedMerged.remove(firstPropertyModel);
+                    unsortedMerged.remove(secondPropertyModel);
+                    if (!firstPropertyModel.isReadable() && !firstPropertyModel.isWritable()) {
+                        unsortedMerged.add(secondPropertyModel);
+                    } else if (!secondPropertyModel.isReadable() && !secondPropertyModel.isWritable()) {
+                        unsortedMerged.add(firstPropertyModel);
+                    } else {
+                        unsortedMerged.add(new PropertyModel(firstPropertyModel, secondPropertyModel));
+                    }
                 }
             }
         }
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/PropertyMismatchTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/PropertyMismatchTest.java
index 8d0e81103..505e9627a 100644
--- a/src/test/java/org/eclipse/yasson/defaultmapping/basic/PropertyMismatchTest.java
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/PropertyMismatchTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0 which is available at
@@ -12,17 +12,20 @@
 
 package org.eclipse.yasson.defaultmapping.basic;
 
-import org.junit.jupiter.api.*;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.eclipse.yasson.Jsonbs.*;
-
+import java.net.URI;
 import java.time.Instant;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Objects;
 
+import jakarta.json.bind.annotation.JsonbProperty;
 import jakarta.json.bind.annotation.JsonbTransient;
-
 import org.jboss.weld.exceptions.IllegalStateException;
+import org.junit.jupiter.api.Test;
+
+import static org.eclipse.yasson.Jsonbs.defaultJsonb;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 /**
  * Tests to verify that read-only properties (properties with no field or setter)
@@ -128,7 +131,65 @@ public void setFoo(Instant instant) {
             this.internalInstantProperty = instant;
         }
     }
-    
+
+    @Test
+    public void testTransientAndPropertyAnnotationMerge() {
+        TransientAndPropertyAnnotationMerge object = new TransientAndPropertyAnnotationMerge();
+        String expected = "{\"number\":\"http://localhost/\"}";
+        String json = defaultJsonb.toJson(object);
+        assertEquals(expected, json);
+        TransientAndPropertyAnnotationMerge deserialized = defaultJsonb.fromJson(expected,
+                                                                                 TransientAndPropertyAnnotationMerge.class);
+        assertEquals(object, deserialized);
+    }
+
+    public static class TransientAndPropertyAnnotationMerge {
+
+        @JsonbTransient
+        private Integer number;
+
+        @JsonbProperty("number")
+        private URI someLink;
+
+        public TransientAndPropertyAnnotationMerge() {
+            number = -1;
+            someLink = URI.create("http://localhost/");
+        }
+
+        public Integer getNumber() {
+            return number;
+        }
+
+        public void setNumber(Integer number) {
+            this.number = number;
+        }
+
+        public URI getSomeLink() {
+            return someLink;
+        }
+
+        public void setSomeLink(URI someLink) {
+            this.someLink = someLink;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            TransientAndPropertyAnnotationMerge that = (TransientAndPropertyAnnotationMerge) o;
+            return Objects.equals(number, that.number) && Objects.equals(someLink, that.someLink);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(number, someLink);
+        }
+    }
+
     /**
      * Test that properties of the same name with different
      * field/getter/setter types behave properly and that we don't