From 5b49d5351c14b5320e24ac6fcb7053c39a080f6b Mon Sep 17 00:00:00 2001 From: Mark Struberg Date: Fri, 2 Feb 2024 14:30:55 +0100 Subject: [PATCH 1/7] [maven-release-plugin] prepare release 4.0.0 --- openjpa-all/pom.xml | 2 +- openjpa-examples/image-gallery/pom.xml | 2 +- openjpa-examples/openbooks/pom.xml | 2 +- openjpa-examples/pom.xml | 2 +- openjpa-examples/simple/pom.xml | 2 +- openjpa-features/pom.xml | 2 +- openjpa-integration/daytrader/pom.xml | 2 +- openjpa-integration/examples/pom.xml | 2 +- openjpa-integration/jmx/pom.xml | 2 +- openjpa-integration/pom.xml | 2 +- openjpa-integration/slf4j/pom.xml | 2 +- openjpa-integration/tck/pom.xml | 2 +- openjpa-integration/validation/pom.xml | 2 +- openjpa-jdbc/pom.xml | 2 +- openjpa-jest/pom.xml | 2 +- openjpa-junit5/pom.xml | 2 +- openjpa-kernel/pom.xml | 2 +- openjpa-kubernetes/pom.xml | 2 +- openjpa-lib/pom.xml | 2 +- openjpa-persistence-jdbc/pom.xml | 2 +- openjpa-persistence-locking/pom.xml | 2 +- openjpa-persistence/pom.xml | 2 +- openjpa-project/pom.xml | 2 +- openjpa-slice/pom.xml | 2 +- openjpa-tools/openjpa-fetch-statistics-was/pom.xml | 2 +- openjpa-tools/openjpa-fetch-statistics/pom.xml | 2 +- openjpa-tools/openjpa-maven-plugin/pom.xml | 2 +- openjpa-tools/pom.xml | 2 +- openjpa-xmlstore/pom.xml | 2 +- openjpa/pom.xml | 2 +- pom.xml | 4 ++-- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/openjpa-all/pom.xml b/openjpa-all/pom.xml index 49dc289d6b..cbf20a324b 100644 --- a/openjpa-all/pom.xml +++ b/openjpa-all/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-all diff --git a/openjpa-examples/image-gallery/pom.xml b/openjpa-examples/image-gallery/pom.xml index 624c23c0dd..5544deb7c8 100644 --- a/openjpa-examples/image-gallery/pom.xml +++ b/openjpa-examples/image-gallery/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-examples - 4.0.0-SNAPSHOT + 4.0.0 org.apache.openjpa.openjpa-examples diff --git a/openjpa-examples/openbooks/pom.xml b/openjpa-examples/openbooks/pom.xml index 770cf061de..5c9b3dee8d 100644 --- a/openjpa-examples/openbooks/pom.xml +++ b/openjpa-examples/openbooks/pom.xml @@ -29,7 +29,7 @@ org.apache.openjpa openjpa-examples - 4.0.0-SNAPSHOT + 4.0.0 org.apache.openjpa.openjpa-examples diff --git a/openjpa-examples/pom.xml b/openjpa-examples/pom.xml index 4ff524ddab..e1eebfa913 100644 --- a/openjpa-examples/pom.xml +++ b/openjpa-examples/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-examples diff --git a/openjpa-examples/simple/pom.xml b/openjpa-examples/simple/pom.xml index db215c0ed5..1ad7560572 100644 --- a/openjpa-examples/simple/pom.xml +++ b/openjpa-examples/simple/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-examples - 4.0.0-SNAPSHOT + 4.0.0 org.apache.openjpa.openjpa-examples diff --git a/openjpa-features/pom.xml b/openjpa-features/pom.xml index d11b455758..3326f0c812 100644 --- a/openjpa-features/pom.xml +++ b/openjpa-features/pom.xml @@ -18,7 +18,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-features diff --git a/openjpa-integration/daytrader/pom.xml b/openjpa-integration/daytrader/pom.xml index eb8a7c3b01..15795d06a8 100644 --- a/openjpa-integration/daytrader/pom.xml +++ b/openjpa-integration/daytrader/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-integration - 4.0.0-SNAPSHOT + 4.0.0 openjpa-integration-daytrader diff --git a/openjpa-integration/examples/pom.xml b/openjpa-integration/examples/pom.xml index d5e45d9de4..512ba1ed95 100644 --- a/openjpa-integration/examples/pom.xml +++ b/openjpa-integration/examples/pom.xml @@ -37,7 +37,7 @@ org.apache.openjpa openjpa-integration - 4.0.0-SNAPSHOT + 4.0.0 openjpa-integration-examples diff --git a/openjpa-integration/jmx/pom.xml b/openjpa-integration/jmx/pom.xml index b82bf9dd06..62d2335c2a 100644 --- a/openjpa-integration/jmx/pom.xml +++ b/openjpa-integration/jmx/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-integration - 4.0.0-SNAPSHOT + 4.0.0 openjpa-integration-jmx diff --git a/openjpa-integration/pom.xml b/openjpa-integration/pom.xml index 04ea6276e4..91d5f7c1f0 100644 --- a/openjpa-integration/pom.xml +++ b/openjpa-integration/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-integration diff --git a/openjpa-integration/slf4j/pom.xml b/openjpa-integration/slf4j/pom.xml index 00f4e747a9..c38a2fc042 100644 --- a/openjpa-integration/slf4j/pom.xml +++ b/openjpa-integration/slf4j/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-integration - 4.0.0-SNAPSHOT + 4.0.0 openjpa-integration-slf4j diff --git a/openjpa-integration/tck/pom.xml b/openjpa-integration/tck/pom.xml index 5d9bcb98b0..44aa267d03 100644 --- a/openjpa-integration/tck/pom.xml +++ b/openjpa-integration/tck/pom.xml @@ -78,7 +78,7 @@ org.apache.openjpa openjpa-integration - 4.0.0-SNAPSHOT + 4.0.0 openjpa-integration-tck diff --git a/openjpa-integration/validation/pom.xml b/openjpa-integration/validation/pom.xml index 5bc45c0da2..2d85428cb3 100644 --- a/openjpa-integration/validation/pom.xml +++ b/openjpa-integration/validation/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-integration - 4.0.0-SNAPSHOT + 4.0.0 openjpa-integration-validation diff --git a/openjpa-jdbc/pom.xml b/openjpa-jdbc/pom.xml index b4a17b1c87..c3f1450527 100644 --- a/openjpa-jdbc/pom.xml +++ b/openjpa-jdbc/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-jdbc diff --git a/openjpa-jest/pom.xml b/openjpa-jest/pom.xml index b2e3732fb9..4604c7ee69 100644 --- a/openjpa-jest/pom.xml +++ b/openjpa-jest/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-jest diff --git a/openjpa-junit5/pom.xml b/openjpa-junit5/pom.xml index 13ab74fdff..72994f35a8 100644 --- a/openjpa-junit5/pom.xml +++ b/openjpa-junit5/pom.xml @@ -24,7 +24,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-junit5 diff --git a/openjpa-kernel/pom.xml b/openjpa-kernel/pom.xml index ea2ebc283d..6face3ef3f 100644 --- a/openjpa-kernel/pom.xml +++ b/openjpa-kernel/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-kernel diff --git a/openjpa-kubernetes/pom.xml b/openjpa-kubernetes/pom.xml index 2707f1a34c..b48671d6c5 100644 --- a/openjpa-kubernetes/pom.xml +++ b/openjpa-kubernetes/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-kubernetes diff --git a/openjpa-lib/pom.xml b/openjpa-lib/pom.xml index b65e00098a..20522ff237 100644 --- a/openjpa-lib/pom.xml +++ b/openjpa-lib/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-lib diff --git a/openjpa-persistence-jdbc/pom.xml b/openjpa-persistence-jdbc/pom.xml index 5223d1c052..19a92e9bfa 100644 --- a/openjpa-persistence-jdbc/pom.xml +++ b/openjpa-persistence-jdbc/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-persistence-jdbc diff --git a/openjpa-persistence-locking/pom.xml b/openjpa-persistence-locking/pom.xml index 4678227b0c..937857b7bf 100644 --- a/openjpa-persistence-locking/pom.xml +++ b/openjpa-persistence-locking/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-persistence-locking diff --git a/openjpa-persistence/pom.xml b/openjpa-persistence/pom.xml index e2e4e81f22..7635991619 100644 --- a/openjpa-persistence/pom.xml +++ b/openjpa-persistence/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-persistence diff --git a/openjpa-project/pom.xml b/openjpa-project/pom.xml index f6963cad6e..bc6487ecd1 100644 --- a/openjpa-project/pom.xml +++ b/openjpa-project/pom.xml @@ -39,7 +39,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 apache-openjpa diff --git a/openjpa-slice/pom.xml b/openjpa-slice/pom.xml index ca0d895f7c..5974a98390 100644 --- a/openjpa-slice/pom.xml +++ b/openjpa-slice/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-slice diff --git a/openjpa-tools/openjpa-fetch-statistics-was/pom.xml b/openjpa-tools/openjpa-fetch-statistics-was/pom.xml index 73d0b5bcb4..b202b50b3a 100644 --- a/openjpa-tools/openjpa-fetch-statistics-was/pom.xml +++ b/openjpa-tools/openjpa-fetch-statistics-was/pom.xml @@ -17,7 +17,7 @@ org.apache.openjpa openjpa-tools - 4.0.0-SNAPSHOT + 4.0.0 openjpa-fetch-statistics-was diff --git a/openjpa-tools/openjpa-fetch-statistics/pom.xml b/openjpa-tools/openjpa-fetch-statistics/pom.xml index 39c03d9b08..0ea24ac406 100644 --- a/openjpa-tools/openjpa-fetch-statistics/pom.xml +++ b/openjpa-tools/openjpa-fetch-statistics/pom.xml @@ -17,7 +17,7 @@ org.apache.openjpa openjpa-tools - 4.0.0-SNAPSHOT + 4.0.0 openjpa-fetch-statistics diff --git a/openjpa-tools/openjpa-maven-plugin/pom.xml b/openjpa-tools/openjpa-maven-plugin/pom.xml index f87277dc0e..a1f4478545 100644 --- a/openjpa-tools/openjpa-maven-plugin/pom.xml +++ b/openjpa-tools/openjpa-maven-plugin/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-tools - 4.0.0-SNAPSHOT + 4.0.0 ../pom.xml diff --git a/openjpa-tools/pom.xml b/openjpa-tools/pom.xml index 44768d7297..4ba314d8fb 100644 --- a/openjpa-tools/pom.xml +++ b/openjpa-tools/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 diff --git a/openjpa-xmlstore/pom.xml b/openjpa-xmlstore/pom.xml index cba5357c79..fbc1ee473e 100644 --- a/openjpa-xmlstore/pom.xml +++ b/openjpa-xmlstore/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa-xmlstore diff --git a/openjpa/pom.xml b/openjpa/pom.xml index 5e2bbe3eb0..2cc93f975c 100644 --- a/openjpa/pom.xml +++ b/openjpa/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0-SNAPSHOT + 4.0.0 openjpa diff --git a/pom.xml b/pom.xml index 91474384b0..6cc21c4dfa 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ OpenJPA Parent POM Apache OpenJPA implementation of Jakarta JPA 3.1 - 4.0.0-SNAPSHOT + 4.0.0 11 @@ -175,7 +175,7 @@ scm:git:https://gitbox.apache.org/repos/asf/openjpa.git scm:git:https://gitbox.apache.org/repos/asf/openjpa.git https://gitbox.apache.org/repos/asf/openjpa.git - HEAD + 4.0.0 From 96005fcd25d5906e3d8c549cf8cb715998f47c13 Mon Sep 17 00:00:00 2001 From: Mark Struberg Date: Fri, 2 Feb 2024 14:30:55 +0100 Subject: [PATCH 2/7] [maven-release-plugin] prepare for next development iteration --- openjpa-all/pom.xml | 2 +- openjpa-examples/image-gallery/pom.xml | 2 +- openjpa-examples/openbooks/pom.xml | 2 +- openjpa-examples/pom.xml | 2 +- openjpa-examples/simple/pom.xml | 2 +- openjpa-features/pom.xml | 2 +- openjpa-integration/daytrader/pom.xml | 2 +- openjpa-integration/examples/pom.xml | 2 +- openjpa-integration/jmx/pom.xml | 2 +- openjpa-integration/pom.xml | 2 +- openjpa-integration/slf4j/pom.xml | 2 +- openjpa-integration/tck/pom.xml | 2 +- openjpa-integration/validation/pom.xml | 2 +- openjpa-jdbc/pom.xml | 2 +- openjpa-jest/pom.xml | 2 +- openjpa-junit5/pom.xml | 2 +- openjpa-kernel/pom.xml | 2 +- openjpa-kubernetes/pom.xml | 2 +- openjpa-lib/pom.xml | 2 +- openjpa-persistence-jdbc/pom.xml | 2 +- openjpa-persistence-locking/pom.xml | 2 +- openjpa-persistence/pom.xml | 2 +- openjpa-project/pom.xml | 2 +- openjpa-slice/pom.xml | 2 +- openjpa-tools/openjpa-fetch-statistics-was/pom.xml | 2 +- openjpa-tools/openjpa-fetch-statistics/pom.xml | 2 +- openjpa-tools/openjpa-maven-plugin/pom.xml | 2 +- openjpa-tools/pom.xml | 2 +- openjpa-xmlstore/pom.xml | 2 +- openjpa/pom.xml | 2 +- pom.xml | 4 ++-- 31 files changed, 32 insertions(+), 32 deletions(-) diff --git a/openjpa-all/pom.xml b/openjpa-all/pom.xml index cbf20a324b..eb0524eb4d 100644 --- a/openjpa-all/pom.xml +++ b/openjpa-all/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-all diff --git a/openjpa-examples/image-gallery/pom.xml b/openjpa-examples/image-gallery/pom.xml index 5544deb7c8..c0b0f8350c 100644 --- a/openjpa-examples/image-gallery/pom.xml +++ b/openjpa-examples/image-gallery/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-examples - 4.0.0 + 4.0.1-SNAPSHOT org.apache.openjpa.openjpa-examples diff --git a/openjpa-examples/openbooks/pom.xml b/openjpa-examples/openbooks/pom.xml index 5c9b3dee8d..b1a6708a3b 100644 --- a/openjpa-examples/openbooks/pom.xml +++ b/openjpa-examples/openbooks/pom.xml @@ -29,7 +29,7 @@ org.apache.openjpa openjpa-examples - 4.0.0 + 4.0.1-SNAPSHOT org.apache.openjpa.openjpa-examples diff --git a/openjpa-examples/pom.xml b/openjpa-examples/pom.xml index e1eebfa913..66b6979d2c 100644 --- a/openjpa-examples/pom.xml +++ b/openjpa-examples/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-examples diff --git a/openjpa-examples/simple/pom.xml b/openjpa-examples/simple/pom.xml index 1ad7560572..ad963161d6 100644 --- a/openjpa-examples/simple/pom.xml +++ b/openjpa-examples/simple/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-examples - 4.0.0 + 4.0.1-SNAPSHOT org.apache.openjpa.openjpa-examples diff --git a/openjpa-features/pom.xml b/openjpa-features/pom.xml index 3326f0c812..4e76e28efa 100644 --- a/openjpa-features/pom.xml +++ b/openjpa-features/pom.xml @@ -18,7 +18,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-features diff --git a/openjpa-integration/daytrader/pom.xml b/openjpa-integration/daytrader/pom.xml index 15795d06a8..5182059b8f 100644 --- a/openjpa-integration/daytrader/pom.xml +++ b/openjpa-integration/daytrader/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-integration - 4.0.0 + 4.0.1-SNAPSHOT openjpa-integration-daytrader diff --git a/openjpa-integration/examples/pom.xml b/openjpa-integration/examples/pom.xml index 512ba1ed95..90b0a330fe 100644 --- a/openjpa-integration/examples/pom.xml +++ b/openjpa-integration/examples/pom.xml @@ -37,7 +37,7 @@ org.apache.openjpa openjpa-integration - 4.0.0 + 4.0.1-SNAPSHOT openjpa-integration-examples diff --git a/openjpa-integration/jmx/pom.xml b/openjpa-integration/jmx/pom.xml index 62d2335c2a..59f4b8c9a8 100644 --- a/openjpa-integration/jmx/pom.xml +++ b/openjpa-integration/jmx/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-integration - 4.0.0 + 4.0.1-SNAPSHOT openjpa-integration-jmx diff --git a/openjpa-integration/pom.xml b/openjpa-integration/pom.xml index 91d5f7c1f0..2907c1bd31 100644 --- a/openjpa-integration/pom.xml +++ b/openjpa-integration/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-integration diff --git a/openjpa-integration/slf4j/pom.xml b/openjpa-integration/slf4j/pom.xml index c38a2fc042..3b73716e7a 100644 --- a/openjpa-integration/slf4j/pom.xml +++ b/openjpa-integration/slf4j/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-integration - 4.0.0 + 4.0.1-SNAPSHOT openjpa-integration-slf4j diff --git a/openjpa-integration/tck/pom.xml b/openjpa-integration/tck/pom.xml index 44aa267d03..828e4d3651 100644 --- a/openjpa-integration/tck/pom.xml +++ b/openjpa-integration/tck/pom.xml @@ -78,7 +78,7 @@ org.apache.openjpa openjpa-integration - 4.0.0 + 4.0.1-SNAPSHOT openjpa-integration-tck diff --git a/openjpa-integration/validation/pom.xml b/openjpa-integration/validation/pom.xml index 2d85428cb3..1322c06503 100644 --- a/openjpa-integration/validation/pom.xml +++ b/openjpa-integration/validation/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-integration - 4.0.0 + 4.0.1-SNAPSHOT openjpa-integration-validation diff --git a/openjpa-jdbc/pom.xml b/openjpa-jdbc/pom.xml index c3f1450527..0a60c5db50 100644 --- a/openjpa-jdbc/pom.xml +++ b/openjpa-jdbc/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-jdbc diff --git a/openjpa-jest/pom.xml b/openjpa-jest/pom.xml index 4604c7ee69..8edf3f0e85 100644 --- a/openjpa-jest/pom.xml +++ b/openjpa-jest/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-jest diff --git a/openjpa-junit5/pom.xml b/openjpa-junit5/pom.xml index 72994f35a8..5caee42e81 100644 --- a/openjpa-junit5/pom.xml +++ b/openjpa-junit5/pom.xml @@ -24,7 +24,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-junit5 diff --git a/openjpa-kernel/pom.xml b/openjpa-kernel/pom.xml index 6face3ef3f..423683213e 100644 --- a/openjpa-kernel/pom.xml +++ b/openjpa-kernel/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-kernel diff --git a/openjpa-kubernetes/pom.xml b/openjpa-kubernetes/pom.xml index b48671d6c5..2e4c31b042 100644 --- a/openjpa-kubernetes/pom.xml +++ b/openjpa-kubernetes/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-kubernetes diff --git a/openjpa-lib/pom.xml b/openjpa-lib/pom.xml index 20522ff237..bd1f034b87 100644 --- a/openjpa-lib/pom.xml +++ b/openjpa-lib/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-lib diff --git a/openjpa-persistence-jdbc/pom.xml b/openjpa-persistence-jdbc/pom.xml index 19a92e9bfa..875427a4c1 100644 --- a/openjpa-persistence-jdbc/pom.xml +++ b/openjpa-persistence-jdbc/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-persistence-jdbc diff --git a/openjpa-persistence-locking/pom.xml b/openjpa-persistence-locking/pom.xml index 937857b7bf..11d512d372 100644 --- a/openjpa-persistence-locking/pom.xml +++ b/openjpa-persistence-locking/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-persistence-locking diff --git a/openjpa-persistence/pom.xml b/openjpa-persistence/pom.xml index 7635991619..746c5c6d6e 100644 --- a/openjpa-persistence/pom.xml +++ b/openjpa-persistence/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-persistence diff --git a/openjpa-project/pom.xml b/openjpa-project/pom.xml index bc6487ecd1..1e021de056 100644 --- a/openjpa-project/pom.xml +++ b/openjpa-project/pom.xml @@ -39,7 +39,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT apache-openjpa diff --git a/openjpa-slice/pom.xml b/openjpa-slice/pom.xml index 5974a98390..8a5c750d43 100644 --- a/openjpa-slice/pom.xml +++ b/openjpa-slice/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-slice diff --git a/openjpa-tools/openjpa-fetch-statistics-was/pom.xml b/openjpa-tools/openjpa-fetch-statistics-was/pom.xml index b202b50b3a..3da9c8b4cf 100644 --- a/openjpa-tools/openjpa-fetch-statistics-was/pom.xml +++ b/openjpa-tools/openjpa-fetch-statistics-was/pom.xml @@ -17,7 +17,7 @@ org.apache.openjpa openjpa-tools - 4.0.0 + 4.0.1-SNAPSHOT openjpa-fetch-statistics-was diff --git a/openjpa-tools/openjpa-fetch-statistics/pom.xml b/openjpa-tools/openjpa-fetch-statistics/pom.xml index 0ea24ac406..f7fb3114d9 100644 --- a/openjpa-tools/openjpa-fetch-statistics/pom.xml +++ b/openjpa-tools/openjpa-fetch-statistics/pom.xml @@ -17,7 +17,7 @@ org.apache.openjpa openjpa-tools - 4.0.0 + 4.0.1-SNAPSHOT openjpa-fetch-statistics diff --git a/openjpa-tools/openjpa-maven-plugin/pom.xml b/openjpa-tools/openjpa-maven-plugin/pom.xml index a1f4478545..6d6081df18 100644 --- a/openjpa-tools/openjpa-maven-plugin/pom.xml +++ b/openjpa-tools/openjpa-maven-plugin/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-tools - 4.0.0 + 4.0.1-SNAPSHOT ../pom.xml diff --git a/openjpa-tools/pom.xml b/openjpa-tools/pom.xml index 4ba314d8fb..ca493b35c7 100644 --- a/openjpa-tools/pom.xml +++ b/openjpa-tools/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT diff --git a/openjpa-xmlstore/pom.xml b/openjpa-xmlstore/pom.xml index fbc1ee473e..b29250a33a 100644 --- a/openjpa-xmlstore/pom.xml +++ b/openjpa-xmlstore/pom.xml @@ -27,7 +27,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa-xmlstore diff --git a/openjpa/pom.xml b/openjpa/pom.xml index 2cc93f975c..a69e10bd44 100644 --- a/openjpa/pom.xml +++ b/openjpa/pom.xml @@ -28,7 +28,7 @@ org.apache.openjpa openjpa-parent - 4.0.0 + 4.0.1-SNAPSHOT openjpa diff --git a/pom.xml b/pom.xml index 6cc21c4dfa..67cc2aa2d1 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ OpenJPA Parent POM Apache OpenJPA implementation of Jakarta JPA 3.1 - 4.0.0 + 4.0.1-SNAPSHOT 11 @@ -175,7 +175,7 @@ scm:git:https://gitbox.apache.org/repos/asf/openjpa.git scm:git:https://gitbox.apache.org/repos/asf/openjpa.git https://gitbox.apache.org/repos/asf/openjpa.git - 4.0.0 + HEAD From 823ffc54e71ce5e1c8582e292147e13e4d2a2c97 Mon Sep 17 00:00:00 2001 From: Mark Struberg Date: Mon, 27 May 2024 18:53:56 +0200 Subject: [PATCH 3/7] OPENJPA-2921 upgrade bundle and karaf maven plugins this fixes the java17 build --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 67cc2aa2d1..41cb23abf5 100644 --- a/pom.xml +++ b/pom.xml @@ -2185,7 +2185,7 @@ org.apache.felix maven-bundle-plugin - 4.0.0 + 5.1.9 true @@ -2198,7 +2198,7 @@ org.apache.karaf.tooling karaf-maven-plugin - 4.2.1 + 4.4.6 org.codehaus.mojo From b3d1ef8671751d47617eaf4ac259b43eae45e53b Mon Sep 17 00:00:00 2001 From: Mark Struberg Date: Mon, 27 May 2024 19:00:40 +0200 Subject: [PATCH 4/7] OPENJPA-2922 disable JNDI lookup of EMF by default To use this feature please set the following system.property to true: emf_via_jndi_enabled --- .../org/apache/openjpa/kernel/localizer.properties | 3 +++ .../apache/openjpa/persistence/OpenJPAPersistence.java | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties index 35271cca8c..f5069ead58 100644 --- a/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties +++ b/openjpa-kernel/src/main/resources/org/apache/openjpa/kernel/localizer.properties @@ -137,6 +137,9 @@ end-trans-error: An exception occurred while ending the transaction. This \ not-bound: The file named "{0}" could not be found. naming-exception: A NamingException was thrown while obtaining the \ factory at "{0}" from JNDI. +jndi-disabled-exception: Accessing the EntityManagerFactory via JNDI has been \ + disabled. Set the system property {0} to 'true' if you want to use this \ + feature. attach-deleted: The object "{0}" with id "{1}" has been deleted and \ cannot be attached. not-detachable: The class "{0}" does not declare the "detachable" metadata \ diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAPersistence.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAPersistence.java index cc7059dcfb..3db4973899 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAPersistence.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OpenJPAPersistence.java @@ -34,6 +34,7 @@ import org.apache.openjpa.lib.conf.ConfigurationProvider; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.util.ImplHelper; +import org.apache.openjpa.util.UserException; /** * Static helper methods for JPA users. @@ -44,6 +45,11 @@ */ public class OpenJPAPersistence { + /** + * Set this System property to 'true' if you want to enable the EntityManager via JNDI + */ + private static final String EMF_VIA_JNDI_ENABLED = "emf_via_jndi_enabled"; + private static final Localizer _loc = Localizer.forPackage(OpenJPAPersistence.class); @@ -137,6 +143,10 @@ public static OpenJPAEntityManagerFactory getEntityManagerFactory(Map map) { */ public static OpenJPAEntityManagerFactory createEntityManagerFactory (String jndiLocation, Context context) { + if (!"true".equalsIgnoreCase(System.getProperty(EMF_VIA_JNDI_ENABLED))) { + throw new UserException(_loc.get("jndi-disabled-exception", EMF_VIA_JNDI_ENABLED)); + } + if (jndiLocation == null) throw new NullPointerException("jndiLocation == null"); From c3fff2cb0ff71a14ed5e2e3eb8242b2fc950db4e Mon Sep 17 00:00:00 2001 From: Maxim Solodovnik Date: Mon, 15 Jul 2024 12:13:57 +0700 Subject: [PATCH 5/7] [OPENJPA-2923] replace new Long(long) with Long.valueOf(long) while enhancing (#117) --- .../apache/openjpa/enhance/PCEnhancer.java | 22 ++++++++++++++----- openjpa-tools/openjpa-maven-plugin/pom.xml | 2 ++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java index 65c5609255..6fd062dfb2 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/PCEnhancer.java @@ -56,6 +56,7 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.conf.OpenJPAConfigurationImpl; @@ -149,6 +150,10 @@ public class PCEnhancer { private static final AuxiliaryEnhancer[] _auxEnhancers; + private static final Method LONG_VALUE_OF = Stream.of(Long.class.getDeclaredMethods()) + .filter(m -> "valueOf".equals(m.getName()) && long.class == m.getParameterTypes()[0]) + .findAny().get(); + static { Class[] classes = Services.getImplementorClasses( AuxiliaryEnhancer.class, @@ -2834,7 +2839,7 @@ private void addCopyKeyFieldsFromObjectIdMethod(boolean fieldManager) throws NoS instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // this } - if (unwrapped != type) { + if (unwrapped != type && type != Long.class) { instructions.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(type))); instructions.add(new InsnNode(Opcodes.DUP)); } @@ -2865,10 +2870,17 @@ else if (oidType == DateId.class) { "getId", Type.getMethodDescriptor(Type.getType(unwrapped)))); if (unwrapped != type) { - instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, - Type.getInternalName(type), - "", - Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(unwrapped)))); + if (type == Long.class) { + instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, + Type.getInternalName(type), + LONG_VALUE_OF.getName(), + Type.getMethodDescriptor(LONG_VALUE_OF))); + } else { + instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, + Type.getInternalName(type), + "", + Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(unwrapped)))); + } } } } diff --git a/openjpa-tools/openjpa-maven-plugin/pom.xml b/openjpa-tools/openjpa-maven-plugin/pom.xml index 6d6081df18..db5cb381f9 100644 --- a/openjpa-tools/openjpa-maven-plugin/pom.xml +++ b/openjpa-tools/openjpa-maven-plugin/pom.xml @@ -75,11 +75,13 @@ org.apache.maven maven-plugin-api ${min.maven.version} + provided org.apache.maven maven-compat ${min.maven.version} + provided org.apache.maven.plugin-tools From ae8c759ffe5ec9bc679e75cf6bd21402c9e8cb21 Mon Sep 17 00:00:00 2001 From: Maxim Solodovnik Date: Mon, 19 Aug 2024 11:13:29 +0700 Subject: [PATCH 6/7] [OPENJPA-2924] BlacklistClassResolver is improved (#118) * BlacklistClassResolver is improved * Comments are addressed --- .../openjpa/util/BlacklistClassResolver.java | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/BlacklistClassResolver.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/BlacklistClassResolver.java index 7e689db1b3..b6ef8aaee7 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/BlacklistClassResolver.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/BlacklistClassResolver.java @@ -18,23 +18,35 @@ */ package org.apache.openjpa.util; +import java.util.stream.Stream; + public class BlacklistClassResolver { + private static final String MATCH_ANY = "*"; + public static final BlacklistClassResolver DEFAULT = new BlacklistClassResolver( toArray(System.getProperty( "openjpa.serialization.class.blacklist", "org.codehaus.groovy.runtime.,org.apache.commons.collections4.functors.,org.apache.xalan")), toArray(System.getProperty("openjpa.serialization.class.whitelist"))); + private final boolean allWhite; + private final boolean allBlack; private final String[] blacklist; private final String[] whitelist; protected BlacklistClassResolver(final String[] blacklist, final String[] whitelist) { + allWhite = Stream.of(whitelist).anyMatch(white -> MATCH_ANY.equals(white)); + allBlack = Stream.of(blacklist).anyMatch(black -> MATCH_ANY.equals(black)); + this.whitelist = whitelist; this.blacklist = blacklist; } protected boolean isBlacklisted(final String name) { - return (whitelist != null && !contains(whitelist, name)) || contains(blacklist, name); + if (allWhite || contains(whitelist, name)) { + return false; + } + return allBlack || contains(blacklist, name); } public final String check(final String name) { @@ -45,15 +57,17 @@ public final String check(final String name) { } private static String[] toArray(final String property) { - return property == null ? null : property.split(" *, *"); + return property == null + ? new String[] {} + : Stream.of(property.split(" *, *")) + .filter(item -> item != null && !item.isEmpty()) + .toArray(String[]::new); } private static boolean contains(final String[] list, String name) { - if (list != null) { - for (final String white : list) { - if (name.startsWith(white)) { - return true; - } + for (final String white : list) { + if (name.startsWith(white)) { + return true; } } return false; From 80a8b7712faa377838cb0276918bad6db59abe85 Mon Sep 17 00:00:00 2001 From: Maxim Solodovnik Date: Wed, 28 Aug 2024 14:18:46 +0700 Subject: [PATCH 7/7] [OPENJPA-2926] Dependencies are updated (#119) --- .gitignore | 1 + openjpa-features/pom.xml | 7 + .../kernel/exps/NullableAggregateUnaryOp.java | 92 +- .../TestSQLStoreParamsSubstitution.java | 186 +- .../conf/SpecificationAdditionalTests.java | 151 +- .../AbstractCollectionDecorator.java | 378 +- .../util/collections/AbstractDualBidiMap.java | 1648 +++--- .../collections/AbstractEmptyIterator.java | 150 +- .../collections/AbstractEmptyMapIterator.java | 94 +- .../util/collections/AbstractHashedMap.java | 2786 +++++----- .../util/collections/AbstractIterableMap.java | 70 +- .../AbstractIteratorDecorator.java | 94 +- .../util/collections/AbstractKeyValue.java | 184 +- .../util/collections/AbstractLinkedMap.java | 1240 ++--- .../collections/AbstractListDecorator.java | 256 +- .../collections/AbstractMapDecorator.java | 312 +- .../util/collections/AbstractMapEntry.java | 184 +- .../AbstractMapEntryDecorator.java | 182 +- .../AbstractOrderedMapDecorator.java | 188 +- .../collections/AbstractReferenceMap.java | 2214 ++++---- .../AbstractSerializableListDecorator.java | 142 +- .../AbstractSerializableSetDecorator.java | 144 +- .../collections/AbstractSetDecorator.java | 148 +- .../AbstractSortedMapDecorator.java | 338 +- .../AbstractUntypedIteratorDecorator.java | 134 +- .../openjpa/lib/util/collections/BidiMap.java | 310 +- .../lib/util/collections/BoundedMap.java | 94 +- .../collections/ComparableComparator.java | 262 +- .../lib/util/collections/ComparatorChain.java | 704 +-- .../lib/util/collections/DefaultMapEntry.java | 122 +- .../lib/util/collections/DualHashBidiMap.java | 218 +- .../lib/util/collections/DualTreeBidiMap.java | 822 +-- .../lib/util/collections/EmptyIterator.java | 142 +- .../util/collections/EmptyMapIterator.java | 106 +- .../collections/EmptyOrderedIterator.java | 102 +- .../collections/EmptyOrderedMapIterator.java | 106 +- .../EntrySetToMapIteratorAdapter.java | 238 +- .../lib/util/collections/FilterIterator.java | 368 +- .../lib/util/collections/IterableMap.java | 174 +- .../util/collections/IterableSortedMap.java | 60 +- .../lib/util/collections/IteratorChain.java | 560 +- .../lib/util/collections/KeyValue.java | 94 +- .../openjpa/lib/util/collections/LRUMap.java | 1060 ++-- .../lib/util/collections/LinkedMap.java | 642 +-- .../util/collections/ListIteratorWrapper.java | 538 +- .../lib/util/collections/MapIterator.java | 228 +- .../lib/util/collections/OrderedBidiMap.java | 104 +- .../lib/util/collections/OrderedIterator.java | 94 +- .../lib/util/collections/OrderedMap.java | 146 +- .../util/collections/OrderedMapIterator.java | 96 +- .../collections/ReferenceIdentityMap.java | 510 +- .../lib/util/collections/ReferenceMap.java | 400 +- .../util/collections/ResettableIterator.java | 76 +- .../collections/ResettableListIterator.java | 64 +- .../lib/util/collections/SortedBidiMap.java | 118 +- .../lib/util/collections/TreeBidiMap.java | 4506 ++++++++--------- .../lib/util/collections/Unmodifiable.java | 76 +- .../collections/UnmodifiableCollection.java | 236 +- .../collections/UnmodifiableEntrySet.java | 380 +- .../collections/UnmodifiableIterator.java | 168 +- .../util/collections/UnmodifiableList.java | 308 +- .../collections/UnmodifiableListIterator.java | 224 +- .../collections/UnmodifiableMapEntry.java | 146 +- .../collections/UnmodifiableOrderedMap.java | 298 +- .../UnmodifiableOrderedMapIterator.java | 222 +- .../lib/util/collections/UnmodifiableSet.java | 234 +- .../jira1794/TestAggregateFunctions.java | 578 +-- .../TestCompatAggregateFunctions.java | 82 +- .../java/org/apache/openjpa/meta/PChild.java | 112 +- .../java/org/apache/openjpa/meta/Parent.java | 104 +- .../openjpa/meta/TestSecondaryTable.java | 98 +- .../EntityListenerMappedSuperClass.java | 58 +- .../callbacks/MSCListenerEntity.java | 100 +- .../callbacks/TestMSCEntityListeners.java | 240 +- .../persistence/compat/Uni_1ToM_Map_FK.java | 182 +- .../TestRemoteQueryCacheCriteriaQuery.java | 122 +- .../criteria/multiselect/DimDay.java | 904 ++-- .../multiselect/FactWorkAssignment.java | 616 +-- .../TestCriteriaMultiselectAliasing.java | 332 +- .../SerializingConcurrentQueryCache.java | 126 +- .../enhance/identity/Dependent5.java | 108 +- .../jdbc/TestIsolationFecthHint.java | 123 +- .../persistence/jdbc/mapping/Authority.java | 158 +- .../jdbc/mapping/TestEnumXmlTypeMapping.java | 78 +- .../jdbc/meta/TestUseSchemaElement.java | 292 +- .../jdbc/query/sub/MaxQueryEntity.java | 138 +- .../jdbc/query/sub/MaxQueryMapEntity.java | 166 +- .../jdbc/query/sub/TestSubQuery.java | 186 +- .../jdbc/sqlcache/CombinedPKTestEntity.java | 160 +- .../sqlcache/discrim/AbstractExtValue.java | 138 +- .../jdbc/sqlcache/discrim/ComposedPK.java | 176 +- .../jdbc/sqlcache/discrim/ExtValue1.java | 62 +- .../discrim/TestFinderCacheWithNulls.java | 226 +- .../jdbc/sqlcache/discrim/UserData.java | 176 +- .../persistence/jpql/joins/Department.java | 212 +- .../persistence/jpql/joins/Employee.java | 196 +- .../TestJoinFetchWithQueryDataCache.java | 490 +- .../jpql/joins/leftfetch/DepartmentTest.java | 128 +- .../jpql/joins/leftfetch/PersonTest.java | 140 +- .../joins/leftfetch/TestJoinLeftFetch.java | 340 +- .../jpql/literals/TestLiteralInSQL.java | 128 +- .../persistence/jpql/version/Author.java | 80 +- .../persistence/jpql/version/Document.java | 76 +- .../jpql/version/TestJoinVersionField.java | 172 +- .../jpql/version/type/BaseEntity.java | 62 +- .../jpql/version/type/ChildVersionEntity.java | 66 +- .../jpql/version/type/LongVersionEntity.java | 83 +- .../type/PrimativeLongVersionEntity.java | 83 +- .../type/PrimativeShortVersionEntity.java | 83 +- .../jpql/version/type/ShortVersionEntity.java | 84 +- .../version/type/TestVersionFieldType.java | 202 +- .../version/type/TimestampVersionEntity.java | 85 +- .../kernel/PessimisticLockEntity.java | 94 +- .../kernel/TestPessimisticLockException.java | 374 +- .../openjpa/persistence/merge/LineItem.java | 284 +- .../openjpa/persistence/merge/LineItemPK.java | 170 +- .../openjpa/persistence/merge/Order.java | 182 +- .../persistence/merge/TestMultipleMerge.java | 176 +- .../timestamp/TestTimestampOptLockEx.java | 258 +- .../optlockex/timestamp/VersionTSEntity.java | 126 +- .../pudefaults/PUDefaultSchemaEntity.java | 94 +- .../PUSchemaInSequenceAnnotationEntity.java | 96 +- .../PUSchemaInSequenceMappingEntity.java | 96 +- .../PUSchemaInTableAnnotationEntity.java | 98 +- .../PUSchemaInTableMappingEntity.java | 102 +- .../TestOpenJPASchemaPUDefault.java | 134 +- .../pudefaults/TestSchemaPUDefault.java | 206 +- .../persistence/querycache/QCEntityM2O.java | 110 +- .../TestQueryCacheWithDataCache.java | 174 +- .../relations/entity/LrsEntityA.java | 202 +- .../relations/entity/LrsEntityB.java | 170 +- .../openjpa/persistence/lockmgr/Person.java | 202 +- .../persistence/lockmgr/VersionEntity.java | 122 +- openjpa-project/pom.xml | 7 + pom.xml | 78 +- 135 files changed, 19398 insertions(+), 19387 deletions(-) diff --git a/.gitignore b/.gitignore index 38e2d26a02..0a94905782 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ dependency-reduced-pom.xml nb-configuration.xml nbactions*.xml .checkstyle +.vscode diff --git a/openjpa-features/pom.xml b/openjpa-features/pom.xml index 4e76e28efa..01e0a7d837 100644 --- a/openjpa-features/pom.xml +++ b/openjpa-features/pom.xml @@ -27,6 +27,13 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + org.apache.karaf.tooling karaf-maven-plugin diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/NullableAggregateUnaryOp.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/NullableAggregateUnaryOp.java index 5955530afb..b09a9e86d0 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/NullableAggregateUnaryOp.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/NullableAggregateUnaryOp.java @@ -1,46 +1,46 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.jdbc.kernel.exps; - -/** - * OPENJPA-1794 - * An aggregate unary operation that can indicate whether a null value from the data store - * should be returned as null. - */ -public abstract class NullableAggregateUnaryOp extends UnaryOp { - private static final long serialVersionUID = 1L; - - public NullableAggregateUnaryOp(Val val) { - super(val); - } - - public NullableAggregateUnaryOp(Val val, boolean noParen) { - super(val, noParen); - } - - @Override - protected boolean nullableValue(ExpContext ctx, ExpState state) { - // If this is a simple operator (no joins involved), check compatibility to determine - // whether 'null' should be returned for the aggregate operation - if (ctx != null && ctx.store != null && (state.joins == null || state.joins.isEmpty())) { - return ctx.store.getConfiguration().getCompatibilityInstance().getReturnNullOnEmptyAggregateResult(); - } - return false; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.jdbc.kernel.exps; + +/** + * OPENJPA-1794 + * An aggregate unary operation that can indicate whether a null value from the data store + * should be returned as null. + */ +public abstract class NullableAggregateUnaryOp extends UnaryOp { + private static final long serialVersionUID = 1L; + + public NullableAggregateUnaryOp(Val val) { + super(val); + } + + public NullableAggregateUnaryOp(Val val, boolean noParen) { + super(val, noParen); + } + + @Override + protected boolean nullableValue(ExpContext ctx, ExpState state) { + // If this is a simple operator (no joins involved), check compatibility to determine + // whether 'null' should be returned for the aggregate operation + if (ctx != null && ctx.store != null && (state.joins == null || state.joins.isEmpty())) { + return ctx.store.getConfiguration().getCompatibilityInstance().getReturnNullOnEmptyAggregateResult(); + } + return false; + } +} diff --git a/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestSQLStoreParamsSubstitution.java b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestSQLStoreParamsSubstitution.java index 8506e69f25..42f8bd1c75 100644 --- a/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestSQLStoreParamsSubstitution.java +++ b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestSQLStoreParamsSubstitution.java @@ -1,93 +1,93 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.jdbc.kernel; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -/** - *

- * Tests SQLStoreQuery.substituteParams() behavior. - *

- */ - -public class TestSQLStoreParamsSubstitution { - - /** - * Tests parameter substitution algorithm to make sure the input sql is NOT transformed - * especially escape characters. - */ - @Test - public void testParamSubstitute() { - - String sqlNrtns[][] = { - { - "like '5@%' escape '@'", - "[]" - }, -// { -// "like '%#####_#%%' escape '#' WHERE ? = 'ab", -// "[1]" -// }, - { - "SELECT 'TRUE' AS VAL FROM DUAL WHERE '\\' = ? AND 'Y' = 'Y'", - "[1]" - }, - { - "like '*_n' escape '*' WHERE x = ? and y = ?", - "[1, 2]" - }, - { - "like '%80@%%' escape '@' WHERE x = ? and y = ? or z=?" , - "[1, 2, 3]" - }, - { - "like '*_sql**%' escape '*" , - "[]" - }, - { "SELECT 'TRUE' AS VAL FROM DUAL WHERE '\\' = ?", - "[1]", - }, - { "SELECT * FROM (" - + "SELECT FOLDER_ID, SYS_CONNECT_BY_PATH(NAME,'\\') AS PATH FROM PROJECT_FOLDER " - + "START WITH PARENT_ID IS NULL CONNECT BY PRIOR FOLDER_ID = PARENT_ID" - + ") WHERE PATH LIKE ?", - "[1]" - } - }; - try { - List paramOrder = new ArrayList<>(); - - for (String sqlNrtn[] : sqlNrtns) { - paramOrder.clear(); - String rtnSql = SQLStoreQuery.substituteParams(sqlNrtn[0], paramOrder); - assertEquals(sqlNrtn[0], rtnSql); - assertEquals(sqlNrtn[1], paramOrder.toString()); - } - } catch (IOException e) { - fail("Unexpected Exception:" + e); - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.jdbc.kernel; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + *

+ * Tests SQLStoreQuery.substituteParams() behavior. + *

+ */ + +public class TestSQLStoreParamsSubstitution { + + /** + * Tests parameter substitution algorithm to make sure the input sql is NOT transformed + * especially escape characters. + */ + @Test + public void testParamSubstitute() { + + String sqlNrtns[][] = { + { + "like '5@%' escape '@'", + "[]" + }, +// { +// "like '%#####_#%%' escape '#' WHERE ? = 'ab", +// "[1]" +// }, + { + "SELECT 'TRUE' AS VAL FROM DUAL WHERE '\\' = ? AND 'Y' = 'Y'", + "[1]" + }, + { + "like '*_n' escape '*' WHERE x = ? and y = ?", + "[1, 2]" + }, + { + "like '%80@%%' escape '@' WHERE x = ? and y = ? or z=?" , + "[1, 2, 3]" + }, + { + "like '*_sql**%' escape '*" , + "[]" + }, + { "SELECT 'TRUE' AS VAL FROM DUAL WHERE '\\' = ?", + "[1]", + }, + { "SELECT * FROM (" + + "SELECT FOLDER_ID, SYS_CONNECT_BY_PATH(NAME,'\\') AS PATH FROM PROJECT_FOLDER " + + "START WITH PARENT_ID IS NULL CONNECT BY PRIOR FOLDER_ID = PARENT_ID" + + ") WHERE PATH LIKE ?", + "[1]" + } + }; + try { + List paramOrder = new ArrayList<>(); + + for (String sqlNrtn[] : sqlNrtns) { + paramOrder.clear(); + String rtnSql = SQLStoreQuery.substituteParams(sqlNrtn[0], paramOrder); + assertEquals(sqlNrtn[0], rtnSql); + assertEquals(sqlNrtn[1], paramOrder.toString()); + } + } catch (IOException e) { + fail("Unexpected Exception:" + e); + } + } +} diff --git a/openjpa-kernel/src/test/java/org/apache/openjpa/conf/SpecificationAdditionalTests.java b/openjpa-kernel/src/test/java/org/apache/openjpa/conf/SpecificationAdditionalTests.java index a258e9aea2..65223d7ea9 100644 --- a/openjpa-kernel/src/test/java/org/apache/openjpa/conf/SpecificationAdditionalTests.java +++ b/openjpa-kernel/src/test/java/org/apache/openjpa/conf/SpecificationAdditionalTests.java @@ -1,77 +1,74 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.conf; - - -import org.junit.Test; -import org.junit.Assert; -import org.apache.openjpa.conf.*; - -public class SpecificationAdditionalTests { - - String myString = "JPA"; - String myCompleteString = "JPA 2.0-draft"; - String myCompleteString2 = "JPA 2.1-draft"; - String myHalfCompleteString = "JPA 2"; - String myHalfCompleteString2 = "JPQ 2"; - String myNullString = null; - - - @Test - public void EqualsTest() { - Specification spec = new Specification(myString); - Assert.assertTrue(spec.equals(spec)); // parse() is overidded in Specification - } - - @Test - public void EqualsTest2() { - Specification spec = new Specification(myString); - Assert.assertFalse(spec.equals(myNullString)); // parse() is overidded in Specification - } - - - @Test - public void EqualsTest3() { - Specification spec = new Specification(myString); - Specification spec2 = new Specification(myHalfCompleteString); - Assert.assertFalse(spec.equals(spec2)); // parse() is overidded in Specification - } - - @Test - public void EqualsTest4() { - Specification spec = new Specification(myString); - Assert.assertFalse(spec.equals(1)); // parse() is overidded in Specification - } - - @Test - public void EqualsTest5() { - Specification spec = new Specification(myHalfCompleteString); - Specification spec2 = new Specification(myHalfCompleteString2); - Assert.assertFalse(spec.equals(spec2)); - } - - @Test - public void EqualsTest6() { - Specification spec = new Specification(myCompleteString); - Specification spec2 = new Specification(myHalfCompleteString2); - Assert.assertFalse(spec.equals(spec2)); - } - - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.conf; + + +import org.junit.Test; +import org.junit.Assert; + +public class SpecificationAdditionalTests { + + String myString = "JPA"; + String myCompleteString = "JPA 2.0-draft"; + String myCompleteString2 = "JPA 2.1-draft"; + String myHalfCompleteString = "JPA 2"; + String myHalfCompleteString2 = "JPQ 2"; + String myNullString = null; + + + @Test + public void EqualsTest() { + Specification spec = new Specification(myString); + Assert.assertTrue(spec.equals(spec)); // parse() is overidded in Specification + } + + @Test + public void EqualsTest2() { + Specification spec = new Specification(myString); + Assert.assertFalse(spec.equals(myNullString)); // parse() is overidded in Specification + } + + + @Test + public void EqualsTest3() { + Specification spec = new Specification(myString); + Specification spec2 = new Specification(myHalfCompleteString); + Assert.assertFalse(spec.equals(spec2)); // parse() is overidded in Specification + } + + @Test + public void EqualsTest4() { + Specification spec = new Specification(myString); + Assert.assertFalse(spec.equals(1)); // parse() is overidded in Specification + } + + @Test + public void EqualsTest5() { + Specification spec = new Specification(myHalfCompleteString); + Specification spec2 = new Specification(myHalfCompleteString2); + Assert.assertFalse(spec.equals(spec2)); + } + + @Test + public void EqualsTest6() { + Specification spec = new Specification(myCompleteString); + Specification spec2 = new Specification(myHalfCompleteString2); + Assert.assertFalse(spec.equals(spec2)); + } +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractCollectionDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractCollectionDecorator.java index 255058656b..cdf2ee7e17 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractCollectionDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractCollectionDecorator.java @@ -1,189 +1,189 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.Serializable; -import java.util.Collection; -import java.util.Iterator; -import java.util.function.Predicate; - -/** - * Decorates another Collection to provide additional behaviour. - *

- * Each method call made on this Collection is forwarded to the - * decorated Collection. This class is used as a framework on which - * to build to extensions such as synchronized and unmodifiable behaviour. The - * main advantage of decoration is that one decorator can wrap any implementation - * of Collection, whereas sub-classing requires a new class to be - * written for each implementation. - *

- *

- * This implementation does not perform any special processing with - * {@link #iterator()}. Instead it simply returns the value from the - * wrapped collection. This may be undesirable, for example if you are trying - * to write an unmodifiable implementation it might provide a loophole. - *

- *

- * This implementation does not forward the hashCode and equals methods through - * to the backing object, but relies on Object's implementation. This is necessary - * to preserve the symmetry of equals. Custom definitions of equality are usually - * based on an interface, such as Set or List, so that the implementation of equals - * can cast the object being tested for equality to the custom interface. - * AbstractCollectionDecorator does not implement such custom interfaces directly; - * they are implemented only in subclasses. Therefore, forwarding equals would break - * symmetry, as the forwarding object might consider itself equal to the object being - * tested, but the reverse could not be true. This behavior is consistent with the - * JDK's collection wrappers, such as {@link java.util.Collections#unmodifiableCollection(Collection)}. - * Use an interface-specific subclass of AbstractCollectionDecorator, such as - * AbstractListDecorator, to preserve equality behavior, or override equals directly. - *

- * - * @param the type of the elements in the collection - * @since 3.0 - */ -public abstract class AbstractCollectionDecorator - implements Collection, Serializable { - - /** Serialization version */ - private static final long serialVersionUID = 6249888059822088500L; - - /** The collection being decorated */ - private Collection collection; - - /** - * Constructor only used in deserialization, do not use otherwise. - * @since 3.1 - */ - protected AbstractCollectionDecorator() { - super(); - } - - /** - * Constructor that wraps (not copies). - * - * @param coll the collection to decorate, must not be null - * @throws NullPointerException if the collection is null - */ - protected AbstractCollectionDecorator(final Collection coll) { - if (coll == null) { - throw new NullPointerException("Collection must not be null."); - } - this.collection = coll; - } - - /** - * Gets the collection being decorated. - * All access to the decorated collection goes via this method. - * - * @return the decorated collection - */ - protected Collection decorated() { - return collection; - } - - /** - * Sets the collection being decorated. - *

- * NOTE: this method should only be used during deserialization - * - * @param coll the decorated collection - */ - protected void setCollection(final Collection coll) { - this.collection = coll; - } - - //----------------------------------------------------------------------- - - @Override - public boolean add(final E object) { - return decorated().add(object); - } - - @Override - public boolean addAll(final Collection coll) { - return decorated().addAll(coll); - } - - @Override - public void clear() { - decorated().clear(); - } - - @Override - public boolean contains(final Object object) { - return decorated().contains(object); - } - - @Override - public boolean isEmpty() { - return decorated().isEmpty(); - } - - @Override - public Iterator iterator() { - return decorated().iterator(); - } - - @Override - public boolean remove(final Object object) { - return decorated().remove(object); - } - - @Override - public int size() { - return decorated().size(); - } - - @Override - public Object[] toArray() { - return decorated().toArray(); - } - - @Override - public T[] toArray(final T[] object) { - return decorated().toArray(object); - } - - @Override - public boolean containsAll(final Collection coll) { - return decorated().containsAll(coll); - } - - /** - * @since 4.4 - */ - @Override - public boolean removeIf(final Predicate filter) { - return decorated().removeIf(filter); - } - - @Override - public boolean removeAll(final Collection coll) { - return decorated().removeAll(coll); - } - - @Override - public boolean retainAll(final Collection coll) { - return decorated().retainAll(coll); - } - - @Override - public String toString() { - return decorated().toString(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Predicate; + +/** + * Decorates another Collection to provide additional behaviour. + *

+ * Each method call made on this Collection is forwarded to the + * decorated Collection. This class is used as a framework on which + * to build to extensions such as synchronized and unmodifiable behaviour. The + * main advantage of decoration is that one decorator can wrap any implementation + * of Collection, whereas sub-classing requires a new class to be + * written for each implementation. + *

+ *

+ * This implementation does not perform any special processing with + * {@link #iterator()}. Instead it simply returns the value from the + * wrapped collection. This may be undesirable, for example if you are trying + * to write an unmodifiable implementation it might provide a loophole. + *

+ *

+ * This implementation does not forward the hashCode and equals methods through + * to the backing object, but relies on Object's implementation. This is necessary + * to preserve the symmetry of equals. Custom definitions of equality are usually + * based on an interface, such as Set or List, so that the implementation of equals + * can cast the object being tested for equality to the custom interface. + * AbstractCollectionDecorator does not implement such custom interfaces directly; + * they are implemented only in subclasses. Therefore, forwarding equals would break + * symmetry, as the forwarding object might consider itself equal to the object being + * tested, but the reverse could not be true. This behavior is consistent with the + * JDK's collection wrappers, such as {@link java.util.Collections#unmodifiableCollection(Collection)}. + * Use an interface-specific subclass of AbstractCollectionDecorator, such as + * AbstractListDecorator, to preserve equality behavior, or override equals directly. + *

+ * + * @param the type of the elements in the collection + * @since 3.0 + */ +public abstract class AbstractCollectionDecorator + implements Collection, Serializable { + + /** Serialization version */ + private static final long serialVersionUID = 6249888059822088500L; + + /** The collection being decorated */ + private Collection collection; + + /** + * Constructor only used in deserialization, do not use otherwise. + * @since 3.1 + */ + protected AbstractCollectionDecorator() { + super(); + } + + /** + * Constructor that wraps (not copies). + * + * @param coll the collection to decorate, must not be null + * @throws NullPointerException if the collection is null + */ + protected AbstractCollectionDecorator(final Collection coll) { + if (coll == null) { + throw new NullPointerException("Collection must not be null."); + } + this.collection = coll; + } + + /** + * Gets the collection being decorated. + * All access to the decorated collection goes via this method. + * + * @return the decorated collection + */ + protected Collection decorated() { + return collection; + } + + /** + * Sets the collection being decorated. + *

+ * NOTE: this method should only be used during deserialization + * + * @param coll the decorated collection + */ + protected void setCollection(final Collection coll) { + this.collection = coll; + } + + //----------------------------------------------------------------------- + + @Override + public boolean add(final E object) { + return decorated().add(object); + } + + @Override + public boolean addAll(final Collection coll) { + return decorated().addAll(coll); + } + + @Override + public void clear() { + decorated().clear(); + } + + @Override + public boolean contains(final Object object) { + return decorated().contains(object); + } + + @Override + public boolean isEmpty() { + return decorated().isEmpty(); + } + + @Override + public Iterator iterator() { + return decorated().iterator(); + } + + @Override + public boolean remove(final Object object) { + return decorated().remove(object); + } + + @Override + public int size() { + return decorated().size(); + } + + @Override + public Object[] toArray() { + return decorated().toArray(); + } + + @Override + public T[] toArray(final T[] object) { + return decorated().toArray(object); + } + + @Override + public boolean containsAll(final Collection coll) { + return decorated().containsAll(coll); + } + + /** + * @since 4.4 + */ + @Override + public boolean removeIf(final Predicate filter) { + return decorated().removeIf(filter); + } + + @Override + public boolean removeAll(final Collection coll) { + return decorated().removeAll(coll); + } + + @Override + public boolean retainAll(final Collection coll) { + return decorated().retainAll(coll); + } + + @Override + public String toString() { + return decorated().toString(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractDualBidiMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractDualBidiMap.java index 39defd27ec..ad5946f997 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractDualBidiMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractDualBidiMap.java @@ -1,824 +1,824 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.function.Predicate; - -/** - * Abstract {@link BidiMap} implemented using two maps. - *

- * An implementation can be written simply by implementing the - * {@link #createBidiMap(Map, Map, BidiMap)} method. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * - * @see DualHashBidiMap - * @see DualTreeBidiMap - * @since 3.0 - */ -public abstract class AbstractDualBidiMap implements BidiMap { - - /** - * Normal delegate map. - */ - transient Map normalMap; - - /** - * Reverse delegate map. - */ - transient Map reverseMap; - - /** - * Inverse view of this map. - */ - transient BidiMap inverseBidiMap = null; - - /** - * View of the keys. - */ - transient Set keySet = null; - - /** - * View of the values. - */ - transient Set values = null; - - /** - * View of the entries. - */ - transient Set> entrySet = null; - - /** - * Creates an empty map, initialised by createMap. - *

- * This constructor remains in place for deserialization. - * All other usage is deprecated in favour of - * {@link #AbstractDualBidiMap(Map, Map)}. - */ - protected AbstractDualBidiMap() { - super(); - } - - /** - * Creates an empty map using the two maps specified as storage. - *

- * The two maps must be a matching pair, normal and reverse. - * They will typically both be empty. - *

- * Neither map is validated, so nulls may be passed in. - * If you choose to do this then the subclass constructor must populate - * the maps[] instance variable itself. - * - * @param normalMap the normal direction map - * @param reverseMap the reverse direction map - * @since 3.1 - */ - protected AbstractDualBidiMap(final Map normalMap, final Map reverseMap) { - super(); - this.normalMap = normalMap; - this.reverseMap = reverseMap; - } - - /** - * Constructs a map that decorates the specified maps, - * used by the subclass createBidiMap implementation. - * - * @param normalMap the normal direction map - * @param reverseMap the reverse direction map - * @param inverseBidiMap the inverse BidiMap - */ - protected AbstractDualBidiMap(final Map normalMap, final Map reverseMap, - final BidiMap inverseBidiMap) { - super(); - this.normalMap = normalMap; - this.reverseMap = reverseMap; - this.inverseBidiMap = inverseBidiMap; - } - - /** - * Creates a new instance of the subclass. - * - * @param normalMap the normal direction map - * @param reverseMap the reverse direction map - * @param inverseMap this map, which is the inverse in the new map - * @return the inverse map - */ - protected abstract BidiMap createBidiMap(Map normalMap, Map reverseMap, BidiMap inverseMap); - - // Map delegation - //----------------------------------------------------------------------- - - @Override - public V get(final Object key) { - return normalMap.get(key); - } - - @Override - public int size() { - return normalMap.size(); - } - - @Override - public boolean isEmpty() { - return normalMap.isEmpty(); - } - - @Override - public boolean containsKey(final Object key) { - return normalMap.containsKey(key); - } - - @Override - public boolean equals(final Object obj) { - return normalMap.equals(obj); - } - - @Override - public int hashCode() { - return normalMap.hashCode(); - } - - @Override - public String toString() { - return normalMap.toString(); - } - - // BidiMap changes - //----------------------------------------------------------------------- - - @Override - public V put(final K key, final V value) { - if (normalMap.containsKey(key)) { - reverseMap.remove(normalMap.get(key)); - } - if (reverseMap.containsKey(value)) { - normalMap.remove(reverseMap.get(value)); - } - final V obj = normalMap.put(key, value); - reverseMap.put(value, key); - return obj; - } - - @Override - public void putAll(final Map map) { - for (final Entry entry : map.entrySet()) { - put(entry.getKey(), entry.getValue()); - } - } - - @Override - public V remove(final Object key) { - V value = null; - if (normalMap.containsKey(key)) { - value = normalMap.remove(key); - reverseMap.remove(value); - } - return value; - } - - @Override - public void clear() { - normalMap.clear(); - reverseMap.clear(); - } - - @Override - public boolean containsValue(final Object value) { - return reverseMap.containsKey(value); - } - - // BidiMap - //----------------------------------------------------------------------- - /** - * Obtains a MapIterator over the map. - * The iterator implements ResetableMapIterator. - * This implementation relies on the entrySet iterator. - *

- * The setValue() methods only allow a new value to be set. - * If the value being set is already in the map, an IllegalArgumentException - * is thrown (as setValue cannot change the size of the map). - * - * @return a map iterator - */ - @Override - public MapIterator mapIterator() { - return new BidiMapIterator<>(this); - } - - @Override - public K getKey(final Object value) { - return reverseMap.get(value); - } - - @Override - public K removeValue(final Object value) { - K key = null; - if (reverseMap.containsKey(value)) { - key = reverseMap.remove(value); - normalMap.remove(key); - } - return key; - } - - @Override - public BidiMap inverseBidiMap() { - if (inverseBidiMap == null) { - inverseBidiMap = createBidiMap(reverseMap, normalMap, this); - } - return inverseBidiMap; - } - - // Map views - //----------------------------------------------------------------------- - /** - * Gets a keySet view of the map. - * Changes made on the view are reflected in the map. - * The set supports remove and clear but not add. - * - * @return the keySet view - */ - @Override - public Set keySet() { - if (keySet == null) { - keySet = new KeySet<>(this); - } - return keySet; - } - - /** - * Creates a key set iterator. - * Subclasses can override this to return iterators with different properties. - * - * @param iterator the iterator to decorate - * @return the keySet iterator - */ - protected Iterator createKeySetIterator(final Iterator iterator) { - return new KeySetIterator<>(iterator, this); - } - - /** - * Gets a values view of the map. - * Changes made on the view are reflected in the map. - * The set supports remove and clear but not add. - * - * @return the values view - */ - @Override - public Set values() { - if (values == null) { - values = new Values<>(this); - } - return values; - } - - /** - * Creates a values iterator. - * Subclasses can override this to return iterators with different properties. - * - * @param iterator the iterator to decorate - * @return the values iterator - */ - protected Iterator createValuesIterator(final Iterator iterator) { - return new ValuesIterator<>(iterator, this); - } - - /** - * Gets an entrySet view of the map. - * Changes made on the set are reflected in the map. - * The set supports remove and clear but not add. - *

- * The Map Entry setValue() method only allow a new value to be set. - * If the value being set is already in the map, an IllegalArgumentException - * is thrown (as setValue cannot change the size of the map). - * - * @return the entrySet view - */ - @Override - public Set> entrySet() { - if (entrySet == null) { - entrySet = new EntrySet<>(this); - } - return entrySet; - } - - /** - * Creates an entry set iterator. - * Subclasses can override this to return iterators with different properties. - * - * @param iterator the iterator to decorate - * @return the entrySet iterator - */ - protected Iterator> createEntrySetIterator(final Iterator> iterator) { - return new EntrySetIterator<>(iterator, this); - } - - //----------------------------------------------------------------------- - /** - * Inner class View. - */ - protected static abstract class View extends AbstractCollectionDecorator { - - /** Generated serial version ID. */ - private static final long serialVersionUID = 4621510560119690639L; - - /** The parent map */ - protected final AbstractDualBidiMap parent; - - /** - * Constructs a new view of the BidiMap. - * - * @param coll the collection view being decorated - * @param parent the parent BidiMap - */ - protected View(final Collection coll, final AbstractDualBidiMap parent) { - super(coll); - this.parent = parent; - } - - @Override - public boolean equals(final Object object) { - return object == this || decorated().equals(object); - } - - @Override - public int hashCode() { - return decorated().hashCode(); - } - - /** - * @since 4.4 - */ - @Override - public boolean removeIf(Predicate filter) { - if (parent.isEmpty() || Objects.isNull(filter)) { - return false; - } - boolean modified = false; - final Iterator it = iterator(); - while (it.hasNext()) { - @SuppressWarnings("unchecked") - final E e = (E) it.next(); - if (filter.test(e)) { - it.remove(); - modified = true; - } - } - return modified; - } - - @Override - public boolean removeAll(final Collection coll) { - if (parent.isEmpty() || coll.isEmpty()) { - return false; - } - boolean modified = false; - final Iterator it = coll.iterator(); - while (it.hasNext()) { - modified |= remove(it.next()); - } - return modified; - } - - /** - * {@inheritDoc} - *

- * This implementation iterates over the elements of this bidi map, checking each element in - * turn to see if it's contained in coll. If it's not contained, it's removed - * from this bidi map. As a consequence, it is advised to use a collection type for - * coll that provides a fast (e.g. O(1)) implementation of - * {@link Collection#contains(Object)}. - */ - @Override - public boolean retainAll(final Collection coll) { - if (parent.isEmpty()) { - return false; - } - if (coll.isEmpty()) { - parent.clear(); - return true; - } - boolean modified = false; - final Iterator it = iterator(); - while (it.hasNext()) { - if (!coll.contains(it.next())) { - it.remove(); - modified = true; - } - } - return modified; - } - - @Override - public void clear() { - parent.clear(); - } - } - - //----------------------------------------------------------------------- - /** - * Inner class KeySet. - */ - protected static class KeySet extends View implements Set { - - /** Serialization version */ - private static final long serialVersionUID = -7107935777385040694L; - - /** - * Constructs a new view of the BidiMap. - * - * @param parent the parent BidiMap - */ - @SuppressWarnings("unchecked") - protected KeySet(final AbstractDualBidiMap parent) { - super(parent.normalMap.keySet(), (AbstractDualBidiMap) parent); - } - - @Override - public Iterator iterator() { - return parent.createKeySetIterator(super.iterator()); - } - - @Override - public boolean contains(final Object key) { - return parent.normalMap.containsKey(key); - } - - @Override - public boolean remove(final Object key) { - if (parent.normalMap.containsKey(key)) { - final Object value = parent.normalMap.remove(key); - parent.reverseMap.remove(value); - return true; - } - return false; - } - } - - /** - * Inner class KeySetIterator. - */ - protected static class KeySetIterator extends AbstractIteratorDecorator { - - /** The parent map */ - protected final AbstractDualBidiMap parent; - - /** The last returned key */ - protected K lastKey = null; - - /** Whether remove is allowed at present */ - protected boolean canRemove = false; - - /** - * Constructor. - * @param iterator the iterator to decorate - * @param parent the parent map - */ - protected KeySetIterator(final Iterator iterator, final AbstractDualBidiMap parent) { - super(iterator); - this.parent = parent; - } - - @Override - public K next() { - lastKey = super.next(); - canRemove = true; - return lastKey; - } - - @Override - public void remove() { - if (!canRemove) { - throw new IllegalStateException("Iterator remove() can only be called once after next()"); - } - final Object value = parent.normalMap.get(lastKey); - super.remove(); - parent.reverseMap.remove(value); - lastKey = null; - canRemove = false; - } - } - - //----------------------------------------------------------------------- - /** - * Inner class Values. - */ - protected static class Values extends View implements Set { - - /** Serialization version */ - private static final long serialVersionUID = 4023777119829639864L; - - /** - * Constructs a new view of the BidiMap. - * - * @param parent the parent BidiMap - */ - @SuppressWarnings("unchecked") - protected Values(final AbstractDualBidiMap parent) { - super(parent.normalMap.values(), (AbstractDualBidiMap) parent); - } - - @Override - public Iterator iterator() { - return parent.createValuesIterator(super.iterator()); - } - - @Override - public boolean contains(final Object value) { - return parent.reverseMap.containsKey(value); - } - - @Override - public boolean remove(final Object value) { - if (parent.reverseMap.containsKey(value)) { - final Object key = parent.reverseMap.remove(value); - parent.normalMap.remove(key); - return true; - } - return false; - } - } - - /** - * Inner class ValuesIterator. - */ - protected static class ValuesIterator extends AbstractIteratorDecorator { - - /** The parent map */ - protected final AbstractDualBidiMap parent; - - /** The last returned value */ - protected V lastValue = null; - - /** Whether remove is allowed at present */ - protected boolean canRemove = false; - - /** - * Constructor. - * @param iterator the iterator to decorate - * @param parent the parent map - */ - @SuppressWarnings("unchecked") - protected ValuesIterator(final Iterator iterator, final AbstractDualBidiMap parent) { - super(iterator); - this.parent = (AbstractDualBidiMap) parent; - } - - @Override - public V next() { - lastValue = super.next(); - canRemove = true; - return lastValue; - } - - @Override - public void remove() { - if (!canRemove) { - throw new IllegalStateException("Iterator remove() can only be called once after next()"); - } - super.remove(); // removes from maps[0] - parent.reverseMap.remove(lastValue); - lastValue = null; - canRemove = false; - } - } - - //----------------------------------------------------------------------- - /** - * Inner class EntrySet. - */ - protected static class EntrySet extends View> implements Set> { - - /** Serialization version */ - private static final long serialVersionUID = 4040410962603292348L; - - /** - * Constructs a new view of the BidiMap. - * - * @param parent the parent BidiMap - */ - protected EntrySet(final AbstractDualBidiMap parent) { - super(parent.normalMap.entrySet(), parent); - } - - @Override - public Iterator> iterator() { - return parent.createEntrySetIterator(super.iterator()); - } - - @Override - public boolean remove(final Object obj) { - if (!(obj instanceof Map.Entry)) { - return false; - } - final Entry entry = (Entry) obj; - final Object key = entry.getKey(); - if (parent.containsKey(key)) { - final V value = parent.normalMap.get(key); - if (value == null ? entry.getValue() == null : value.equals(entry.getValue())) { - parent.normalMap.remove(key); - parent.reverseMap.remove(value); - return true; - } - } - return false; - } - } - - /** - * Inner class EntrySetIterator. - */ - protected static class EntrySetIterator extends AbstractIteratorDecorator> { - - /** The parent map */ - protected final AbstractDualBidiMap parent; - - /** The last returned entry */ - protected Entry last = null; - - /** Whether remove is allowed at present */ - protected boolean canRemove = false; - - /** - * Constructor. - * @param iterator the iterator to decorate - * @param parent the parent map - */ - protected EntrySetIterator(final Iterator> iterator, final AbstractDualBidiMap parent) { - super(iterator); - this.parent = parent; - } - - @Override - public Entry next() { - last = new MapEntry<>(super.next(), parent); - canRemove = true; - return last; - } - - @Override - public void remove() { - if (!canRemove) { - throw new IllegalStateException("Iterator remove() can only be called once after next()"); - } - // store value as remove may change the entry in the decorator (eg.TreeMap) - final Object value = last.getValue(); - super.remove(); - parent.reverseMap.remove(value); - last = null; - canRemove = false; - } - } - - /** - * Inner class MapEntry. - */ - protected static class MapEntry extends AbstractMapEntryDecorator { - - /** The parent map */ - protected final AbstractDualBidiMap parent; - - /** - * Constructor. - * @param entry the entry to decorate - * @param parent the parent map - */ - protected MapEntry(final Entry entry, final AbstractDualBidiMap parent) { - super(entry); - this.parent = parent; - } - - @Override - public V setValue(final V value) { - final K key = MapEntry.this.getKey(); - if (parent.reverseMap.containsKey(value) && - parent.reverseMap.get(value) != key) { - throw new IllegalArgumentException( - "Cannot use setValue() when the object being set is already in the map"); - } - parent.put(key, value); - return super.setValue(value); - } - } - - /** - * Inner class MapIterator. - */ - protected static class BidiMapIterator implements MapIterator, ResettableIterator { - - /** The parent map */ - protected final AbstractDualBidiMap parent; - - /** The iterator being wrapped */ - protected Iterator> iterator; - - /** The last returned entry */ - protected Entry last = null; - - /** Whether remove is allowed at present */ - protected boolean canRemove = false; - - /** - * Constructor. - * @param parent the parent map - */ - protected BidiMapIterator(final AbstractDualBidiMap parent) { - super(); - this.parent = parent; - this.iterator = parent.normalMap.entrySet().iterator(); - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public K next() { - last = iterator.next(); - canRemove = true; - return last.getKey(); - } - - @Override - public void remove() { - if (!canRemove) { - throw new IllegalStateException("Iterator remove() can only be called once after next()"); - } - // store value as remove may change the entry in the decorator (eg.TreeMap) - final V value = last.getValue(); - iterator.remove(); - parent.reverseMap.remove(value); - last = null; - canRemove = false; - } - - @Override - public K getKey() { - if (last == null) { - throw new IllegalStateException( - "Iterator getKey() can only be called after next() and before remove()"); - } - return last.getKey(); - } - - @Override - public V getValue() { - if (last == null) { - throw new IllegalStateException( - "Iterator getValue() can only be called after next() and before remove()"); - } - return last.getValue(); - } - - @Override - public V setValue(final V value) { - if (last == null) { - throw new IllegalStateException( - "Iterator setValue() can only be called after next() and before remove()"); - } - if (parent.reverseMap.containsKey(value) && - parent.reverseMap.get(value) != last.getKey()) { - throw new IllegalArgumentException( - "Cannot use setValue() when the object being set is already in the map"); - } - return parent.put(last.getKey(), value); - } - - @Override - public void reset() { - iterator = parent.normalMap.entrySet().iterator(); - last = null; - canRemove = false; - } - - @Override - public String toString() { - if (last != null) { - return "MapIterator[" + getKey() + "=" + getValue() + "]"; - } - return "MapIterator[]"; - } - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; + +/** + * Abstract {@link BidiMap} implemented using two maps. + *

+ * An implementation can be written simply by implementing the + * {@link #createBidiMap(Map, Map, BidiMap)} method. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * + * @see DualHashBidiMap + * @see DualTreeBidiMap + * @since 3.0 + */ +public abstract class AbstractDualBidiMap implements BidiMap { + + /** + * Normal delegate map. + */ + transient Map normalMap; + + /** + * Reverse delegate map. + */ + transient Map reverseMap; + + /** + * Inverse view of this map. + */ + transient BidiMap inverseBidiMap = null; + + /** + * View of the keys. + */ + transient Set keySet = null; + + /** + * View of the values. + */ + transient Set values = null; + + /** + * View of the entries. + */ + transient Set> entrySet = null; + + /** + * Creates an empty map, initialised by createMap. + *

+ * This constructor remains in place for deserialization. + * All other usage is deprecated in favour of + * {@link #AbstractDualBidiMap(Map, Map)}. + */ + protected AbstractDualBidiMap() { + super(); + } + + /** + * Creates an empty map using the two maps specified as storage. + *

+ * The two maps must be a matching pair, normal and reverse. + * They will typically both be empty. + *

+ * Neither map is validated, so nulls may be passed in. + * If you choose to do this then the subclass constructor must populate + * the maps[] instance variable itself. + * + * @param normalMap the normal direction map + * @param reverseMap the reverse direction map + * @since 3.1 + */ + protected AbstractDualBidiMap(final Map normalMap, final Map reverseMap) { + super(); + this.normalMap = normalMap; + this.reverseMap = reverseMap; + } + + /** + * Constructs a map that decorates the specified maps, + * used by the subclass createBidiMap implementation. + * + * @param normalMap the normal direction map + * @param reverseMap the reverse direction map + * @param inverseBidiMap the inverse BidiMap + */ + protected AbstractDualBidiMap(final Map normalMap, final Map reverseMap, + final BidiMap inverseBidiMap) { + super(); + this.normalMap = normalMap; + this.reverseMap = reverseMap; + this.inverseBidiMap = inverseBidiMap; + } + + /** + * Creates a new instance of the subclass. + * + * @param normalMap the normal direction map + * @param reverseMap the reverse direction map + * @param inverseMap this map, which is the inverse in the new map + * @return the inverse map + */ + protected abstract BidiMap createBidiMap(Map normalMap, Map reverseMap, BidiMap inverseMap); + + // Map delegation + //----------------------------------------------------------------------- + + @Override + public V get(final Object key) { + return normalMap.get(key); + } + + @Override + public int size() { + return normalMap.size(); + } + + @Override + public boolean isEmpty() { + return normalMap.isEmpty(); + } + + @Override + public boolean containsKey(final Object key) { + return normalMap.containsKey(key); + } + + @Override + public boolean equals(final Object obj) { + return normalMap.equals(obj); + } + + @Override + public int hashCode() { + return normalMap.hashCode(); + } + + @Override + public String toString() { + return normalMap.toString(); + } + + // BidiMap changes + //----------------------------------------------------------------------- + + @Override + public V put(final K key, final V value) { + if (normalMap.containsKey(key)) { + reverseMap.remove(normalMap.get(key)); + } + if (reverseMap.containsKey(value)) { + normalMap.remove(reverseMap.get(value)); + } + final V obj = normalMap.put(key, value); + reverseMap.put(value, key); + return obj; + } + + @Override + public void putAll(final Map map) { + for (final Entry entry : map.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public V remove(final Object key) { + V value = null; + if (normalMap.containsKey(key)) { + value = normalMap.remove(key); + reverseMap.remove(value); + } + return value; + } + + @Override + public void clear() { + normalMap.clear(); + reverseMap.clear(); + } + + @Override + public boolean containsValue(final Object value) { + return reverseMap.containsKey(value); + } + + // BidiMap + //----------------------------------------------------------------------- + /** + * Obtains a MapIterator over the map. + * The iterator implements ResetableMapIterator. + * This implementation relies on the entrySet iterator. + *

+ * The setValue() methods only allow a new value to be set. + * If the value being set is already in the map, an IllegalArgumentException + * is thrown (as setValue cannot change the size of the map). + * + * @return a map iterator + */ + @Override + public MapIterator mapIterator() { + return new BidiMapIterator<>(this); + } + + @Override + public K getKey(final Object value) { + return reverseMap.get(value); + } + + @Override + public K removeValue(final Object value) { + K key = null; + if (reverseMap.containsKey(value)) { + key = reverseMap.remove(value); + normalMap.remove(key); + } + return key; + } + + @Override + public BidiMap inverseBidiMap() { + if (inverseBidiMap == null) { + inverseBidiMap = createBidiMap(reverseMap, normalMap, this); + } + return inverseBidiMap; + } + + // Map views + //----------------------------------------------------------------------- + /** + * Gets a keySet view of the map. + * Changes made on the view are reflected in the map. + * The set supports remove and clear but not add. + * + * @return the keySet view + */ + @Override + public Set keySet() { + if (keySet == null) { + keySet = new KeySet<>(this); + } + return keySet; + } + + /** + * Creates a key set iterator. + * Subclasses can override this to return iterators with different properties. + * + * @param iterator the iterator to decorate + * @return the keySet iterator + */ + protected Iterator createKeySetIterator(final Iterator iterator) { + return new KeySetIterator<>(iterator, this); + } + + /** + * Gets a values view of the map. + * Changes made on the view are reflected in the map. + * The set supports remove and clear but not add. + * + * @return the values view + */ + @Override + public Set values() { + if (values == null) { + values = new Values<>(this); + } + return values; + } + + /** + * Creates a values iterator. + * Subclasses can override this to return iterators with different properties. + * + * @param iterator the iterator to decorate + * @return the values iterator + */ + protected Iterator createValuesIterator(final Iterator iterator) { + return new ValuesIterator<>(iterator, this); + } + + /** + * Gets an entrySet view of the map. + * Changes made on the set are reflected in the map. + * The set supports remove and clear but not add. + *

+ * The Map Entry setValue() method only allow a new value to be set. + * If the value being set is already in the map, an IllegalArgumentException + * is thrown (as setValue cannot change the size of the map). + * + * @return the entrySet view + */ + @Override + public Set> entrySet() { + if (entrySet == null) { + entrySet = new EntrySet<>(this); + } + return entrySet; + } + + /** + * Creates an entry set iterator. + * Subclasses can override this to return iterators with different properties. + * + * @param iterator the iterator to decorate + * @return the entrySet iterator + */ + protected Iterator> createEntrySetIterator(final Iterator> iterator) { + return new EntrySetIterator<>(iterator, this); + } + + //----------------------------------------------------------------------- + /** + * Inner class View. + */ + protected static abstract class View extends AbstractCollectionDecorator { + + /** Generated serial version ID. */ + private static final long serialVersionUID = 4621510560119690639L; + + /** The parent map */ + protected final AbstractDualBidiMap parent; + + /** + * Constructs a new view of the BidiMap. + * + * @param coll the collection view being decorated + * @param parent the parent BidiMap + */ + protected View(final Collection coll, final AbstractDualBidiMap parent) { + super(coll); + this.parent = parent; + } + + @Override + public boolean equals(final Object object) { + return object == this || decorated().equals(object); + } + + @Override + public int hashCode() { + return decorated().hashCode(); + } + + /** + * @since 4.4 + */ + @Override + public boolean removeIf(Predicate filter) { + if (parent.isEmpty() || Objects.isNull(filter)) { + return false; + } + boolean modified = false; + final Iterator it = iterator(); + while (it.hasNext()) { + @SuppressWarnings("unchecked") + final E e = (E) it.next(); + if (filter.test(e)) { + it.remove(); + modified = true; + } + } + return modified; + } + + @Override + public boolean removeAll(final Collection coll) { + if (parent.isEmpty() || coll.isEmpty()) { + return false; + } + boolean modified = false; + final Iterator it = coll.iterator(); + while (it.hasNext()) { + modified |= remove(it.next()); + } + return modified; + } + + /** + * {@inheritDoc} + *

+ * This implementation iterates over the elements of this bidi map, checking each element in + * turn to see if it's contained in coll. If it's not contained, it's removed + * from this bidi map. As a consequence, it is advised to use a collection type for + * coll that provides a fast (e.g. O(1)) implementation of + * {@link Collection#contains(Object)}. + */ + @Override + public boolean retainAll(final Collection coll) { + if (parent.isEmpty()) { + return false; + } + if (coll.isEmpty()) { + parent.clear(); + return true; + } + boolean modified = false; + final Iterator it = iterator(); + while (it.hasNext()) { + if (!coll.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + @Override + public void clear() { + parent.clear(); + } + } + + //----------------------------------------------------------------------- + /** + * Inner class KeySet. + */ + protected static class KeySet extends View implements Set { + + /** Serialization version */ + private static final long serialVersionUID = -7107935777385040694L; + + /** + * Constructs a new view of the BidiMap. + * + * @param parent the parent BidiMap + */ + @SuppressWarnings("unchecked") + protected KeySet(final AbstractDualBidiMap parent) { + super(parent.normalMap.keySet(), (AbstractDualBidiMap) parent); + } + + @Override + public Iterator iterator() { + return parent.createKeySetIterator(super.iterator()); + } + + @Override + public boolean contains(final Object key) { + return parent.normalMap.containsKey(key); + } + + @Override + public boolean remove(final Object key) { + if (parent.normalMap.containsKey(key)) { + final Object value = parent.normalMap.remove(key); + parent.reverseMap.remove(value); + return true; + } + return false; + } + } + + /** + * Inner class KeySetIterator. + */ + protected static class KeySetIterator extends AbstractIteratorDecorator { + + /** The parent map */ + protected final AbstractDualBidiMap parent; + + /** The last returned key */ + protected K lastKey = null; + + /** Whether remove is allowed at present */ + protected boolean canRemove = false; + + /** + * Constructor. + * @param iterator the iterator to decorate + * @param parent the parent map + */ + protected KeySetIterator(final Iterator iterator, final AbstractDualBidiMap parent) { + super(iterator); + this.parent = parent; + } + + @Override + public K next() { + lastKey = super.next(); + canRemove = true; + return lastKey; + } + + @Override + public void remove() { + if (!canRemove) { + throw new IllegalStateException("Iterator remove() can only be called once after next()"); + } + final Object value = parent.normalMap.get(lastKey); + super.remove(); + parent.reverseMap.remove(value); + lastKey = null; + canRemove = false; + } + } + + //----------------------------------------------------------------------- + /** + * Inner class Values. + */ + protected static class Values extends View implements Set { + + /** Serialization version */ + private static final long serialVersionUID = 4023777119829639864L; + + /** + * Constructs a new view of the BidiMap. + * + * @param parent the parent BidiMap + */ + @SuppressWarnings("unchecked") + protected Values(final AbstractDualBidiMap parent) { + super(parent.normalMap.values(), (AbstractDualBidiMap) parent); + } + + @Override + public Iterator iterator() { + return parent.createValuesIterator(super.iterator()); + } + + @Override + public boolean contains(final Object value) { + return parent.reverseMap.containsKey(value); + } + + @Override + public boolean remove(final Object value) { + if (parent.reverseMap.containsKey(value)) { + final Object key = parent.reverseMap.remove(value); + parent.normalMap.remove(key); + return true; + } + return false; + } + } + + /** + * Inner class ValuesIterator. + */ + protected static class ValuesIterator extends AbstractIteratorDecorator { + + /** The parent map */ + protected final AbstractDualBidiMap parent; + + /** The last returned value */ + protected V lastValue = null; + + /** Whether remove is allowed at present */ + protected boolean canRemove = false; + + /** + * Constructor. + * @param iterator the iterator to decorate + * @param parent the parent map + */ + @SuppressWarnings("unchecked") + protected ValuesIterator(final Iterator iterator, final AbstractDualBidiMap parent) { + super(iterator); + this.parent = (AbstractDualBidiMap) parent; + } + + @Override + public V next() { + lastValue = super.next(); + canRemove = true; + return lastValue; + } + + @Override + public void remove() { + if (!canRemove) { + throw new IllegalStateException("Iterator remove() can only be called once after next()"); + } + super.remove(); // removes from maps[0] + parent.reverseMap.remove(lastValue); + lastValue = null; + canRemove = false; + } + } + + //----------------------------------------------------------------------- + /** + * Inner class EntrySet. + */ + protected static class EntrySet extends View> implements Set> { + + /** Serialization version */ + private static final long serialVersionUID = 4040410962603292348L; + + /** + * Constructs a new view of the BidiMap. + * + * @param parent the parent BidiMap + */ + protected EntrySet(final AbstractDualBidiMap parent) { + super(parent.normalMap.entrySet(), parent); + } + + @Override + public Iterator> iterator() { + return parent.createEntrySetIterator(super.iterator()); + } + + @Override + public boolean remove(final Object obj) { + if (!(obj instanceof Map.Entry)) { + return false; + } + final Entry entry = (Entry) obj; + final Object key = entry.getKey(); + if (parent.containsKey(key)) { + final V value = parent.normalMap.get(key); + if (value == null ? entry.getValue() == null : value.equals(entry.getValue())) { + parent.normalMap.remove(key); + parent.reverseMap.remove(value); + return true; + } + } + return false; + } + } + + /** + * Inner class EntrySetIterator. + */ + protected static class EntrySetIterator extends AbstractIteratorDecorator> { + + /** The parent map */ + protected final AbstractDualBidiMap parent; + + /** The last returned entry */ + protected Entry last = null; + + /** Whether remove is allowed at present */ + protected boolean canRemove = false; + + /** + * Constructor. + * @param iterator the iterator to decorate + * @param parent the parent map + */ + protected EntrySetIterator(final Iterator> iterator, final AbstractDualBidiMap parent) { + super(iterator); + this.parent = parent; + } + + @Override + public Entry next() { + last = new MapEntry<>(super.next(), parent); + canRemove = true; + return last; + } + + @Override + public void remove() { + if (!canRemove) { + throw new IllegalStateException("Iterator remove() can only be called once after next()"); + } + // store value as remove may change the entry in the decorator (eg.TreeMap) + final Object value = last.getValue(); + super.remove(); + parent.reverseMap.remove(value); + last = null; + canRemove = false; + } + } + + /** + * Inner class MapEntry. + */ + protected static class MapEntry extends AbstractMapEntryDecorator { + + /** The parent map */ + protected final AbstractDualBidiMap parent; + + /** + * Constructor. + * @param entry the entry to decorate + * @param parent the parent map + */ + protected MapEntry(final Entry entry, final AbstractDualBidiMap parent) { + super(entry); + this.parent = parent; + } + + @Override + public V setValue(final V value) { + final K key = MapEntry.this.getKey(); + if (parent.reverseMap.containsKey(value) && + parent.reverseMap.get(value) != key) { + throw new IllegalArgumentException( + "Cannot use setValue() when the object being set is already in the map"); + } + parent.put(key, value); + return super.setValue(value); + } + } + + /** + * Inner class MapIterator. + */ + protected static class BidiMapIterator implements MapIterator, ResettableIterator { + + /** The parent map */ + protected final AbstractDualBidiMap parent; + + /** The iterator being wrapped */ + protected Iterator> iterator; + + /** The last returned entry */ + protected Entry last = null; + + /** Whether remove is allowed at present */ + protected boolean canRemove = false; + + /** + * Constructor. + * @param parent the parent map + */ + protected BidiMapIterator(final AbstractDualBidiMap parent) { + super(); + this.parent = parent; + this.iterator = parent.normalMap.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public K next() { + last = iterator.next(); + canRemove = true; + return last.getKey(); + } + + @Override + public void remove() { + if (!canRemove) { + throw new IllegalStateException("Iterator remove() can only be called once after next()"); + } + // store value as remove may change the entry in the decorator (eg.TreeMap) + final V value = last.getValue(); + iterator.remove(); + parent.reverseMap.remove(value); + last = null; + canRemove = false; + } + + @Override + public K getKey() { + if (last == null) { + throw new IllegalStateException( + "Iterator getKey() can only be called after next() and before remove()"); + } + return last.getKey(); + } + + @Override + public V getValue() { + if (last == null) { + throw new IllegalStateException( + "Iterator getValue() can only be called after next() and before remove()"); + } + return last.getValue(); + } + + @Override + public V setValue(final V value) { + if (last == null) { + throw new IllegalStateException( + "Iterator setValue() can only be called after next() and before remove()"); + } + if (parent.reverseMap.containsKey(value) && + parent.reverseMap.get(value) != last.getKey()) { + throw new IllegalArgumentException( + "Cannot use setValue() when the object being set is already in the map"); + } + return parent.put(last.getKey(), value); + } + + @Override + public void reset() { + iterator = parent.normalMap.entrySet().iterator(); + last = null; + canRemove = false; + } + + @Override + public String toString() { + if (last != null) { + return "MapIterator[" + getKey() + "=" + getValue() + "]"; + } + return "MapIterator[]"; + } + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractEmptyIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractEmptyIterator.java index d7c5910d03..6f5e4d42da 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractEmptyIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractEmptyIterator.java @@ -1,75 +1,75 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.NoSuchElementException; - -/** - * Provides an implementation of an empty iterator. - * - * @since 3.1 - */ -abstract class AbstractEmptyIterator { - - /** - * Constructor. - */ - protected AbstractEmptyIterator() { - super(); - } - - public boolean hasNext() { - return false; - } - - public E next() { - throw new NoSuchElementException("Iterator contains no elements"); - } - - public boolean hasPrevious() { - return false; - } - - public E previous() { - throw new NoSuchElementException("Iterator contains no elements"); - } - - public int nextIndex() { - return 0; - } - - public int previousIndex() { - return -1; - } - - public void add(final E obj) { - throw new UnsupportedOperationException("add() not supported for empty Iterator"); - } - - public void set(final E obj) { - throw new IllegalStateException("Iterator contains no elements"); - } - - public void remove() { - throw new IllegalStateException("Iterator contains no elements"); - } - - public void reset() { - // do nothing - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.NoSuchElementException; + +/** + * Provides an implementation of an empty iterator. + * + * @since 3.1 + */ +abstract class AbstractEmptyIterator { + + /** + * Constructor. + */ + protected AbstractEmptyIterator() { + super(); + } + + public boolean hasNext() { + return false; + } + + public E next() { + throw new NoSuchElementException("Iterator contains no elements"); + } + + public boolean hasPrevious() { + return false; + } + + public E previous() { + throw new NoSuchElementException("Iterator contains no elements"); + } + + public int nextIndex() { + return 0; + } + + public int previousIndex() { + return -1; + } + + public void add(final E obj) { + throw new UnsupportedOperationException("add() not supported for empty Iterator"); + } + + public void set(final E obj) { + throw new IllegalStateException("Iterator contains no elements"); + } + + public void remove() { + throw new IllegalStateException("Iterator contains no elements"); + } + + public void reset() { + // do nothing + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractEmptyMapIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractEmptyMapIterator.java index cf7684b04e..66365087bb 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractEmptyMapIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractEmptyMapIterator.java @@ -1,47 +1,47 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Provides an implementation of an empty map iterator. - * - * @param the type of keys - * @param the type of mapped values - * @since 4.0 - */ -public abstract class AbstractEmptyMapIterator extends AbstractEmptyIterator { - - /** - * Create a new AbstractEmptyMapIterator. - */ - public AbstractEmptyMapIterator() { - super(); - } - - public K getKey() { - throw new IllegalStateException("Iterator contains no elements"); - } - - public V getValue() { - throw new IllegalStateException("Iterator contains no elements"); - } - - public V setValue(final V value) { - throw new IllegalStateException("Iterator contains no elements"); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Provides an implementation of an empty map iterator. + * + * @param the type of keys + * @param the type of mapped values + * @since 4.0 + */ +public abstract class AbstractEmptyMapIterator extends AbstractEmptyIterator { + + /** + * Create a new AbstractEmptyMapIterator. + */ + public AbstractEmptyMapIterator() { + super(); + } + + public K getKey() { + throw new IllegalStateException("Iterator contains no elements"); + } + + public V getValue() { + throw new IllegalStateException("Iterator contains no elements"); + } + + public V setValue(final V value) { + throw new IllegalStateException("Iterator contains no elements"); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractHashedMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractHashedMap.java index c24f950272..03f6754042 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractHashedMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractHashedMap.java @@ -1,1393 +1,1393 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.AbstractCollection; -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -/** - * An abstract implementation of a hash-based map which provides numerous points for - * subclasses to override. - *

- * This class implements all the features necessary for a subclass hash-based map. - * Key-value entries are stored in instances of the HashEntry class, - * which can be overridden and replaced. The iterators can similarly be replaced, - * without the need to replace the KeySet, EntrySet and Values view classes. - *

- * Overridable methods are provided to change the default hashing behaviour, and - * to change how entries are added to and removed from the map. Hopefully, all you - * need for unusual subclasses is here. - *

- * NOTE: From Commons Collections 3.1 this class extends AbstractMap. - * This is to provide backwards compatibility for ReferenceMap between v3.0 and v3.1. - * This extends clause will be removed in v5.0. - * - * @param the type of the keys in this map - * @param the type of the values in this map - * @since 3.0 - */ -public class AbstractHashedMap extends AbstractMap implements IterableMap { - - protected static final String NO_NEXT_ENTRY = "No next() entry in the iteration"; - protected static final String NO_PREVIOUS_ENTRY = "No previous() entry in the iteration"; - protected static final String REMOVE_INVALID = "remove() can only be called once after next()"; - protected static final String GETKEY_INVALID = "getKey() can only be called after next() and before remove()"; - protected static final String GETVALUE_INVALID = "getValue() can only be called after next() and before remove()"; - protected static final String SETVALUE_INVALID = "setValue() can only be called after next() and before remove()"; - - /** The default capacity to use */ - protected static final int DEFAULT_CAPACITY = 16; - /** The default threshold to use */ - protected static final int DEFAULT_THRESHOLD = 12; - /** The default load factor to use */ - protected static final float DEFAULT_LOAD_FACTOR = 0.75f; - /** The maximum capacity allowed */ - protected static final int MAXIMUM_CAPACITY = 1 << 30; - /** An object for masking null */ - protected static final Object NULL = new Object(); - - /** Load factor, normally 0.75 */ - transient float loadFactor; - /** The size of the map */ - transient int size; - /** Map entries */ - transient HashEntry[] data; - /** Size at which to rehash */ - transient int threshold; - /** Modification count for iterators */ - transient int modCount; - /** Entry set */ - transient EntrySet entrySet; - /** Key set */ - transient KeySet keySet; - /** Values */ - transient Values values; - - /** - * Constructor only used in deserialization, do not use otherwise. - */ - protected AbstractHashedMap() { - super(); - } - - /** - * Constructor which performs no validation on the passed in parameters. - * - * @param initialCapacity the initial capacity, must be a power of two - * @param loadFactor the load factor, must be > 0.0f and generally < 1.0f - * @param threshold the threshold, must be sensible - */ - @SuppressWarnings("unchecked") - protected AbstractHashedMap(final int initialCapacity, final float loadFactor, final int threshold) { - super(); - this.loadFactor = loadFactor; - this.data = new HashEntry[initialCapacity]; - this.threshold = threshold; - init(); - } - - /** - * Constructs a new, empty map with the specified initial capacity and - * default load factor. - * - * @param initialCapacity the initial capacity - * @throws IllegalArgumentException if the initial capacity is negative - */ - protected AbstractHashedMap(final int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR); - } - - /** - * Constructs a new, empty map with the specified initial capacity and - * load factor. - * - * @param initialCapacity the initial capacity - * @param loadFactor the load factor - * @throws IllegalArgumentException if the initial capacity is negative - * @throws IllegalArgumentException if the load factor is less than or equal to zero - */ - @SuppressWarnings("unchecked") - protected AbstractHashedMap(int initialCapacity, final float loadFactor) { - super(); - if (initialCapacity < 0) { - throw new IllegalArgumentException("Initial capacity must be a non negative number"); - } - if (loadFactor <= 0.0f || Float.isNaN(loadFactor)) { - throw new IllegalArgumentException("Load factor must be greater than 0"); - } - this.loadFactor = loadFactor; - initialCapacity = calculateNewCapacity(initialCapacity); - this.threshold = calculateThreshold(initialCapacity, loadFactor); - this.data = new HashEntry[initialCapacity]; - init(); - } - - /** - * Constructor copying elements from another map. - * - * @param map the map to copy - * @throws NullPointerException if the map is null - */ - protected AbstractHashedMap(final Map map) { - this(Math.max(2 * map.size(), DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); - _putAll(map); - } - - /** - * Initialise subclasses during construction, cloning or deserialization. - */ - protected void init() { - } - - //----------------------------------------------------------------------- - /** - * Gets the value mapped to the key specified. - * - * @param key the key - * @return the mapped value, null if no match - */ - @Override - public V get(Object key) { - key = convertKey(key); - final int hashCode = hash(key); - HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index - while (entry != null) { - if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { - return entry.getValue(); - } - entry = entry.next; - } - return null; - } - - /** - * Gets the size of the map. - * - * @return the size - */ - @Override - public int size() { - return size; - } - - /** - * Checks whether the map is currently empty. - * - * @return true if the map is currently size zero - */ - @Override - public boolean isEmpty() { - return size == 0; - } - - //----------------------------------------------------------------------- - /** - * Checks whether the map contains the specified key. - * - * @param key the key to search for - * @return true if the map contains the key - */ - @Override - public boolean containsKey(Object key) { - key = convertKey(key); - final int hashCode = hash(key); - HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index - while (entry != null) { - if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { - return true; - } - entry = entry.next; - } - return false; - } - - /** - * Checks whether the map contains the specified value. - * - * @param value the value to search for - * @return true if the map contains the value - */ - @Override - public boolean containsValue(final Object value) { - if (value == null) { - for (final HashEntry element : data) { - HashEntry entry = element; - while (entry != null) { - if (entry.getValue() == null) { - return true; - } - entry = entry.next; - } - } - } else { - for (final HashEntry element : data) { - HashEntry entry = element; - while (entry != null) { - if (isEqualValue(value, entry.getValue())) { - return true; - } - entry = entry.next; - } - } - } - return false; - } - - //----------------------------------------------------------------------- - /** - * Puts a key-value mapping into this map. - * - * @param key the key to add - * @param value the value to add - * @return the value previously mapped to this key, null if none - */ - @Override - public V put(final K key, final V value) { - final Object convertedKey = convertKey(key); - final int hashCode = hash(convertedKey); - final int index = hashIndex(hashCode, data.length); - HashEntry entry = data[index]; - while (entry != null) { - if (entry.hashCode == hashCode && isEqualKey(convertedKey, entry.key)) { - final V oldValue = entry.getValue(); - updateEntry(entry, value); - return oldValue; - } - entry = entry.next; - } - - addMapping(index, hashCode, key, value); - return null; - } - - /** - * Puts all the values from the specified map into this map. - *

- * This implementation iterates around the specified map and - * uses {@link #put(Object, Object)}. - * - * @param map the map to add - * @throws NullPointerException if the map is null - */ - @Override - public void putAll(final Map map) { - _putAll(map); - } - - /** - * Puts all the values from the specified map into this map. - *

- * This implementation iterates around the specified map and - * uses {@link #put(Object, Object)}. - *

- * It is private to allow the constructor to still call it - * even when putAll is overriden. - * - * @param map the map to add - * @throws NullPointerException if the map is null - */ - private void _putAll(final Map map) { - final int mapSize = map.size(); - if (mapSize == 0) { - return; - } - final int newSize = (int) ((size + mapSize) / loadFactor + 1); - ensureCapacity(calculateNewCapacity(newSize)); - for (final Entry entry: map.entrySet()) { - put(entry.getKey(), entry.getValue()); - } - } - - /** - * Removes the specified mapping from this map. - * - * @param key the mapping to remove - * @return the value mapped to the removed key, null if key not in map - */ - @Override - public V remove(Object key) { - key = convertKey(key); - final int hashCode = hash(key); - final int index = hashIndex(hashCode, data.length); - HashEntry entry = data[index]; - HashEntry previous = null; - while (entry != null) { - if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { - final V oldValue = entry.getValue(); - removeMapping(entry, index, previous); - return oldValue; - } - previous = entry; - entry = entry.next; - } - return null; - } - - /** - * Clears the map, resetting the size to zero and nullifying references - * to avoid garbage collection issues. - */ - @Override - public void clear() { - modCount++; - final HashEntry[] data = this.data; - for (int i = data.length - 1; i >= 0; i--) { - data[i] = null; - } - size = 0; - } - - //----------------------------------------------------------------------- - /** - * Converts input keys to another object for storage in the map. - * This implementation masks nulls. - * Subclasses can override this to perform alternate key conversions. - *

- * The reverse conversion can be changed, if required, by overriding the - * getKey() method in the hash entry. - * - * @param key the key convert - * @return the converted key - */ - protected Object convertKey(final Object key) { - return key == null ? NULL : key; - } - - /** - * Gets the hash code for the key specified. - * This implementation uses the additional hashing routine from JDK1.4. - * Subclasses can override this to return alternate hash codes. - * - * @param key the key to get a hash code for - * @return the hash code - */ - protected int hash(final Object key) { - // same as JDK 1.4 - int h = key.hashCode(); - h += ~(h << 9); - h ^= h >>> 14; - h += h << 4; - h ^= h >>> 10; - return h; - } - - /** - * Compares two keys, in internal converted form, to see if they are equal. - * This implementation uses the equals method and assumes neither key is null. - * Subclasses can override this to match differently. - * - * @param key1 the first key to compare passed in from outside - * @param key2 the second key extracted from the entry via entry.key - * @return true if equal - */ - protected boolean isEqualKey(final Object key1, final Object key2) { - return key1 == key2 || key1.equals(key2); - } - - /** - * Compares two values, in external form, to see if they are equal. - * This implementation uses the equals method and assumes neither value is null. - * Subclasses can override this to match differently. - * - * @param value1 the first value to compare passed in from outside - * @param value2 the second value extracted from the entry via getValue() - * @return true if equal - */ - protected boolean isEqualValue(final Object value1, final Object value2) { - return value1 == value2 || value1.equals(value2); - } - - /** - * Gets the index into the data storage for the hashCode specified. - * This implementation uses the least significant bits of the hashCode. - * Subclasses can override this to return alternate bucketing. - * - * @param hashCode the hash code to use - * @param dataSize the size of the data to pick a bucket from - * @return the bucket index - */ - protected int hashIndex(final int hashCode, final int dataSize) { - return hashCode & dataSize - 1; - } - - //----------------------------------------------------------------------- - /** - * Gets the entry mapped to the key specified. - *

- * This method exists for subclasses that may need to perform a multi-step - * process accessing the entry. The public methods in this class don't use this - * method to gain a small performance boost. - * - * @param key the key - * @return the entry, null if no match - */ - protected HashEntry getEntry(Object key) { - key = convertKey(key); - final int hashCode = hash(key); - HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index - while (entry != null) { - if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { - return entry; - } - entry = entry.next; - } - return null; - } - - //----------------------------------------------------------------------- - /** - * Updates an existing key-value mapping to change the value. - *

- * This implementation calls setValue() on the entry. - * Subclasses could override to handle changes to the map. - * - * @param entry the entry to update - * @param newValue the new value to store - */ - protected void updateEntry(final HashEntry entry, final V newValue) { - entry.setValue(newValue); - } - - /** - * Reuses an existing key-value mapping, storing completely new data. - *

- * This implementation sets all the data fields on the entry. - * Subclasses could populate additional entry fields. - * - * @param entry the entry to update, not null - * @param hashIndex the index in the data array - * @param hashCode the hash code of the key to add - * @param key the key to add - * @param value the value to add - */ - protected void reuseEntry(final HashEntry entry, final int hashIndex, final int hashCode, - final K key, final V value) { - entry.next = data[hashIndex]; - entry.hashCode = hashCode; - entry.key = key; - entry.value = value; - } - - //----------------------------------------------------------------------- - /** - * Adds a new key-value mapping into this map. - *

- * This implementation calls createEntry(), addEntry() - * and checkCapacity(). - * It also handles changes to modCount and size. - * Subclasses could override to fully control adds to the map. - * - * @param hashIndex the index into the data array to store at - * @param hashCode the hash code of the key to add - * @param key the key to add - * @param value the value to add - */ - protected void addMapping(final int hashIndex, final int hashCode, final K key, final V value) { - modCount++; - final HashEntry entry = createEntry(data[hashIndex], hashCode, key, value); - addEntry(entry, hashIndex); - size++; - checkCapacity(); - } - - /** - * Creates an entry to store the key-value data. - *

- * This implementation creates a new HashEntry instance. - * Subclasses can override this to return a different storage class, - * or implement caching. - * - * @param next the next entry in sequence - * @param hashCode the hash code to use - * @param key the key to store - * @param value the value to store - * @return the newly created entry - */ - protected HashEntry createEntry(final HashEntry next, final int hashCode, final K key, final V value) { - return new HashEntry<>(next, hashCode, convertKey(key), value); - } - - /** - * Adds an entry into this map. - *

- * This implementation adds the entry to the data storage table. - * Subclasses could override to handle changes to the map. - * - * @param entry the entry to add - * @param hashIndex the index into the data array to store at - */ - protected void addEntry(final HashEntry entry, final int hashIndex) { - data[hashIndex] = entry; - } - - //----------------------------------------------------------------------- - /** - * Removes a mapping from the map. - *

- * This implementation calls removeEntry() and destroyEntry(). - * It also handles changes to modCount and size. - * Subclasses could override to fully control removals from the map. - * - * @param entry the entry to remove - * @param hashIndex the index into the data structure - * @param previous the previous entry in the chain - */ - protected void removeMapping(final HashEntry entry, final int hashIndex, final HashEntry previous) { - modCount++; - removeEntry(entry, hashIndex, previous); - size--; - destroyEntry(entry); - } - - /** - * Removes an entry from the chain stored in a particular index. - *

- * This implementation removes the entry from the data storage table. - * The size is not updated. - * Subclasses could override to handle changes to the map. - * - * @param entry the entry to remove - * @param hashIndex the index into the data structure - * @param previous the previous entry in the chain - */ - protected void removeEntry(final HashEntry entry, final int hashIndex, final HashEntry previous) { - if (previous == null) { - data[hashIndex] = entry.next; - } else { - previous.next = entry.next; - } - } - - /** - * Kills an entry ready for the garbage collector. - *

- * This implementation prepares the HashEntry for garbage collection. - * Subclasses can override this to implement caching (override clear as well). - * - * @param entry the entry to destroy - */ - protected void destroyEntry(final HashEntry entry) { - entry.next = null; - entry.key = null; - entry.value = null; - } - - //----------------------------------------------------------------------- - /** - * Checks the capacity of the map and enlarges it if necessary. - *

- * This implementation uses the threshold to check if the map needs enlarging - */ - protected void checkCapacity() { - if (size >= threshold) { - final int newCapacity = data.length * 2; - if (newCapacity <= MAXIMUM_CAPACITY) { - ensureCapacity(newCapacity); - } - } - } - - /** - * Changes the size of the data structure to the capacity proposed. - * - * @param newCapacity the new capacity of the array (a power of two, less or equal to max) - */ - @SuppressWarnings("unchecked") - protected void ensureCapacity(final int newCapacity) { - final int oldCapacity = data.length; - if (newCapacity <= oldCapacity) { - return; - } - if (size == 0) { - threshold = calculateThreshold(newCapacity, loadFactor); - data = new HashEntry[newCapacity]; - } else { - final HashEntry oldEntries[] = data; - final HashEntry newEntries[] = new HashEntry[newCapacity]; - - modCount++; - for (int i = oldCapacity - 1; i >= 0; i--) { - HashEntry entry = oldEntries[i]; - if (entry != null) { - oldEntries[i] = null; // gc - do { - final HashEntry next = entry.next; - final int index = hashIndex(entry.hashCode, newCapacity); - entry.next = newEntries[index]; - newEntries[index] = entry; - entry = next; - } while (entry != null); - } - } - threshold = calculateThreshold(newCapacity, loadFactor); - data = newEntries; - } - } - - /** - * Calculates the new capacity of the map. - * This implementation normalizes the capacity to a power of two. - * - * @param proposedCapacity the proposed capacity - * @return the normalized new capacity - */ - protected int calculateNewCapacity(final int proposedCapacity) { - int newCapacity = 1; - if (proposedCapacity > MAXIMUM_CAPACITY) { - newCapacity = MAXIMUM_CAPACITY; - } else { - while (newCapacity < proposedCapacity) { - newCapacity <<= 1; // multiply by two - } - if (newCapacity > MAXIMUM_CAPACITY) { - newCapacity = MAXIMUM_CAPACITY; - } - } - return newCapacity; - } - - /** - * Calculates the new threshold of the map, where it will be resized. - * This implementation uses the load factor. - * - * @param newCapacity the new capacity - * @param factor the load factor - * @return the new resize threshold - */ - protected int calculateThreshold(final int newCapacity, final float factor) { - return (int) (newCapacity * factor); - } - - //----------------------------------------------------------------------- - /** - * Gets the next field from a HashEntry. - * Used in subclasses that have no visibility of the field. - * - * @param entry the entry to query, must not be null - * @return the next field of the entry - * @throws NullPointerException if the entry is null - * @since 3.1 - */ - protected HashEntry entryNext(final HashEntry entry) { - return entry.next; - } - - /** - * Gets the hashCode field from a HashEntry. - * Used in subclasses that have no visibility of the field. - * - * @param entry the entry to query, must not be null - * @return the hashCode field of the entry - * @throws NullPointerException if the entry is null - * @since 3.1 - */ - protected int entryHashCode(final HashEntry entry) { - return entry.hashCode; - } - - /** - * Gets the key field from a HashEntry. - * Used in subclasses that have no visibility of the field. - * - * @param entry the entry to query, must not be null - * @return the key field of the entry - * @throws NullPointerException if the entry is null - * @since 3.1 - */ - protected K entryKey(final HashEntry entry) { - return entry.getKey(); - } - - /** - * Gets the value field from a HashEntry. - * Used in subclasses that have no visibility of the field. - * - * @param entry the entry to query, must not be null - * @return the value field of the entry - * @throws NullPointerException if the entry is null - * @since 3.1 - */ - protected V entryValue(final HashEntry entry) { - return entry.getValue(); - } - - //----------------------------------------------------------------------- - /** - * Gets an iterator over the map. - * Changes made to the iterator affect this map. - *

- * A MapIterator returns the keys in the map. It also provides convenient - * methods to get the key and value, and set the value. - * It avoids the need to create an entrySet/keySet/values object. - * It also avoids creating the Map.Entry object. - * - * @return the map iterator - */ - @Override - public MapIterator mapIterator() { - if (size == 0) { - return EmptyMapIterator.emptyMapIterator(); - } - return new HashMapIterator<>(this); - } - - /** - * MapIterator implementation. - */ - protected static class HashMapIterator extends HashIterator implements MapIterator { - - protected HashMapIterator(final AbstractHashedMap parent) { - super(parent); - } - - @Override - public K next() { - return super.nextEntry().getKey(); - } - - @Override - public K getKey() { - final HashEntry current = currentEntry(); - if (current == null) { - throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); - } - return current.getKey(); - } - - @Override - public V getValue() { - final HashEntry current = currentEntry(); - if (current == null) { - throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); - } - return current.getValue(); - } - - @Override - public V setValue(final V value) { - final HashEntry current = currentEntry(); - if (current == null) { - throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); - } - return current.setValue(value); - } - } - - //----------------------------------------------------------------------- - /** - * Gets the entrySet view of the map. - * Changes made to the view affect this map. - * To simply iterate through the entries, use {@link #mapIterator()}. - * - * @return the entrySet view - */ - @Override - public Set> entrySet() { - if (entrySet == null) { - entrySet = new EntrySet<>(this); - } - return entrySet; - } - - /** - * Creates an entry set iterator. - * Subclasses can override this to return iterators with different properties. - * - * @return the entrySet iterator - */ - protected Iterator> createEntrySetIterator() { - if (size() == 0) { - return EmptyIterator.>emptyIterator(); - } - return new EntrySetIterator<>(this); - } - - /** - * EntrySet implementation. - */ - protected static class EntrySet extends AbstractSet> { - /** The parent map */ - private final AbstractHashedMap parent; - - protected EntrySet(final AbstractHashedMap parent) { - super(); - this.parent = parent; - } - - @Override - public int size() { - return parent.size(); - } - - @Override - public void clear() { - parent.clear(); - } - - @Override - public boolean contains(final Object entry) { - if (entry instanceof Map.Entry) { - final Entry e = (Entry) entry; - final Entry match = parent.getEntry(e.getKey()); - return match != null && match.equals(e); - } - return false; - } - - @Override - public boolean remove(final Object obj) { - if (!(obj instanceof Map.Entry)) { - return false; - } - if (!contains(obj)) { - return false; - } - final Entry entry = (Entry) obj; - parent.remove(entry.getKey()); - return true; - } - - @Override - public Iterator> iterator() { - return parent.createEntrySetIterator(); - } - } - - /** - * EntrySet iterator. - */ - protected static class EntrySetIterator extends HashIterator implements Iterator> { - - protected EntrySetIterator(final AbstractHashedMap parent) { - super(parent); - } - - @Override - public Entry next() { - return super.nextEntry(); - } - } - - //----------------------------------------------------------------------- - /** - * Gets the keySet view of the map. - * Changes made to the view affect this map. - * To simply iterate through the keys, use {@link #mapIterator()}. - * - * @return the keySet view - */ - @Override - public Set keySet() { - if (keySet == null) { - keySet = new KeySet<>(this); - } - return keySet; - } - - /** - * Creates a key set iterator. - * Subclasses can override this to return iterators with different properties. - * - * @return the keySet iterator - */ - protected Iterator createKeySetIterator() { - if (size() == 0) { - return EmptyIterator.emptyIterator(); - } - return new KeySetIterator<>(this); - } - - /** - * KeySet implementation. - */ - protected static class KeySet extends AbstractSet { - /** The parent map */ - private final AbstractHashedMap parent; - - protected KeySet(final AbstractHashedMap parent) { - super(); - this.parent = parent; - } - - @Override - public int size() { - return parent.size(); - } - - @Override - public void clear() { - parent.clear(); - } - - @Override - public boolean contains(final Object key) { - return parent.containsKey(key); - } - - @Override - public boolean remove(final Object key) { - final boolean result = parent.containsKey(key); - parent.remove(key); - return result; - } - - @Override - public Iterator iterator() { - return parent.createKeySetIterator(); - } - } - - /** - * KeySet iterator. - */ - protected static class KeySetIterator extends HashIterator implements Iterator { - - @SuppressWarnings("unchecked") - protected KeySetIterator(final AbstractHashedMap parent) { - super((AbstractHashedMap) parent); - } - - @Override - public K next() { - return super.nextEntry().getKey(); - } - } - - //----------------------------------------------------------------------- - /** - * Gets the values view of the map. - * Changes made to the view affect this map. - * To simply iterate through the values, use {@link #mapIterator()}. - * - * @return the values view - */ - @Override - public Collection values() { - if (values == null) { - values = new Values<>(this); - } - return values; - } - - /** - * Creates a values iterator. - * Subclasses can override this to return iterators with different properties. - * - * @return the values iterator - */ - protected Iterator createValuesIterator() { - if (size() == 0) { - return EmptyIterator.emptyIterator(); - } - return new ValuesIterator<>(this); - } - - /** - * Values implementation. - */ - protected static class Values extends AbstractCollection { - /** The parent map */ - private final AbstractHashedMap parent; - - protected Values(final AbstractHashedMap parent) { - super(); - this.parent = parent; - } - - @Override - public int size() { - return parent.size(); - } - - @Override - public void clear() { - parent.clear(); - } - - @Override - public boolean contains(final Object value) { - return parent.containsValue(value); - } - - @Override - public Iterator iterator() { - return parent.createValuesIterator(); - } - } - - /** - * Values iterator. - */ - protected static class ValuesIterator extends HashIterator implements Iterator { - - @SuppressWarnings("unchecked") - protected ValuesIterator(final AbstractHashedMap parent) { - super((AbstractHashedMap) parent); - } - - @Override - public V next() { - return super.nextEntry().getValue(); - } - } - - //----------------------------------------------------------------------- - /** - * HashEntry used to store the data. - *

- * If you subclass AbstractHashedMap but not HashEntry - * then you will not be able to access the protected fields. - * The entryXxx() methods on AbstractHashedMap exist - * to provide the necessary access. - */ - protected static class HashEntry implements Entry, KeyValue { - /** The next entry in the hash chain */ - protected HashEntry next; - /** The hash code of the key */ - protected int hashCode; - /** The key */ - protected Object key; - /** The value */ - protected Object value; - - protected HashEntry(final HashEntry next, final int hashCode, final Object key, final V value) { - super(); - this.next = next; - this.hashCode = hashCode; - this.key = key; - this.value = value; - } - - @Override - @SuppressWarnings("unchecked") - public K getKey() { - if (key == NULL) { - return null; - } - return (K) key; - } - - @Override - @SuppressWarnings("unchecked") - public V getValue() { - return (V) value; - } - - @Override - @SuppressWarnings("unchecked") - public V setValue(final V value) { - final Object old = this.value; - this.value = value; - return (V) old; - } - - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Map.Entry)) { - return false; - } - final Entry other = (Entry) obj; - return - (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) && - (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())); - } - - @Override - public int hashCode() { - return (getKey() == null ? 0 : getKey().hashCode()) ^ - (getValue() == null ? 0 : getValue().hashCode()); - } - - @Override - public String toString() { - return new StringBuilder().append(getKey()).append('=').append(getValue()).toString(); - } - } - - /** - * Base Iterator - */ - protected static abstract class HashIterator { - - /** The parent map */ - private final AbstractHashedMap parent; - /** The current index into the array of buckets */ - private int hashIndex; - /** The last returned entry */ - private HashEntry last; - /** The next entry */ - private HashEntry next; - /** The modification count expected */ - private int expectedModCount; - - protected HashIterator(final AbstractHashedMap parent) { - super(); - this.parent = parent; - final HashEntry[] data = parent.data; - int i = data.length; - HashEntry next = null; - while (i > 0 && next == null) { - next = data[--i]; - } - this.next = next; - this.hashIndex = i; - this.expectedModCount = parent.modCount; - } - - public boolean hasNext() { - return next != null; - } - - protected HashEntry nextEntry() { - if (parent.modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - final HashEntry newCurrent = next; - if (newCurrent == null) { - throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY); - } - final HashEntry[] data = parent.data; - int i = hashIndex; - HashEntry n = newCurrent.next; - while (n == null && i > 0) { - n = data[--i]; - } - next = n; - hashIndex = i; - last = newCurrent; - return newCurrent; - } - - protected HashEntry currentEntry() { - return last; - } - - public void remove() { - if (last == null) { - throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID); - } - if (parent.modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - parent.remove(last.getKey()); - last = null; - expectedModCount = parent.modCount; - } - - @Override - public String toString() { - if (last != null) { - return "Iterator[" + last.getKey() + "=" + last.getValue() + "]"; - } - return "Iterator[]"; - } - } - - //----------------------------------------------------------------------- - /** - * Writes the map data to the stream. This method must be overridden if a - * subclass must be setup before put() is used. - *

- * Serialization is not one of the JDK's nicest topics. Normal serialization will - * initialise the superclass before the subclass. Sometimes however, this isn't - * what you want, as in this case the put() method on read can be - * affected by subclass state. - *

- * The solution adopted here is to serialize the state data of this class in - * this protected method. This method must be called by the - * writeObject() of the first serializable subclass. - *

- * Subclasses may override if they have a specific field that must be present - * on read before this implementation will work. Generally, the read determines - * what must be serialized here, if anything. - * - * @param out the output stream - * @throws IOException if an error occurs while writing tothe stream - */ - protected void doWriteObject(final ObjectOutputStream out) throws IOException { - out.writeFloat(loadFactor); - out.writeInt(data.length); - out.writeInt(size); - for (final MapIterator it = mapIterator(); it.hasNext();) { - out.writeObject(it.next()); - out.writeObject(it.getValue()); - } - } - - /** - * Reads the map data from the stream. This method must be overridden if a - * subclass must be setup before put() is used. - *

- * Serialization is not one of the JDK's nicest topics. Normal serialization will - * initialise the superclass before the subclass. Sometimes however, this isn't - * what you want, as in this case the put() method on read can be - * affected by subclass state. - *

- * The solution adopted here is to deserialize the state data of this class in - * this protected method. This method must be called by the - * readObject() of the first serializable subclass. - *

- * Subclasses may override if the subclass has a specific field that must be present - * before put() or calculateThreshold() will work correctly. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - @SuppressWarnings("unchecked") - protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - loadFactor = in.readFloat(); - final int capacity = in.readInt(); - final int size = in.readInt(); - init(); - threshold = calculateThreshold(capacity, loadFactor); - data = new HashEntry[capacity]; - for (int i = 0; i < size; i++) { - final K key = (K) in.readObject(); - final V value = (V) in.readObject(); - put(key, value); - } - } - - //----------------------------------------------------------------------- - /** - * Clones the map without cloning the keys or values. - *

- * To implement clone(), a subclass must implement the - * Cloneable interface and make this method public. - * - * @return a shallow clone - * @throws InternalError if {@link AbstractMap#clone()} failed - */ - @Override - @SuppressWarnings("unchecked") - protected AbstractHashedMap clone() { - try { - final AbstractHashedMap cloned = (AbstractHashedMap) super.clone(); - cloned.data = new HashEntry[data.length]; - cloned.entrySet = null; - cloned.keySet = null; - cloned.values = null; - cloned.modCount = 0; - cloned.size = 0; - cloned.init(); - cloned.putAll(this); - return cloned; - } catch (final CloneNotSupportedException ex) { - throw new InternalError(); - } - } - - /** - * Compares this map with another. - * - * @param obj the object to compare to - * @return true if equal - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Map)) { - return false; - } - final Map map = (Map) obj; - if (map.size() != size()) { - return false; - } - final MapIterator it = mapIterator(); - try { - while (it.hasNext()) { - final Object key = it.next(); - final Object value = it.getValue(); - if (value == null) { - if (map.get(key) != null || !map.containsKey(key)) { - return false; - } - } else { - if (!value.equals(map.get(key))) { - return false; - } - } - } - } catch (final ClassCastException | NullPointerException ignored) { - return false; - } - return true; - } - - /** - * Gets the standard Map hashCode. - * - * @return the hash code defined in the Map interface - */ - @Override - public int hashCode() { - int total = 0; - final Iterator> it = createEntrySetIterator(); - while (it.hasNext()) { - total += it.next().hashCode(); - } - return total; - } - - /** - * Gets the map as a String. - * - * @return a string version of the map - */ - @Override - public String toString() { - if (size() == 0) { - return "{}"; - } - final StringBuilder buf = new StringBuilder(32 * size()); - buf.append('{'); - - final MapIterator it = mapIterator(); - boolean hasNext = it.hasNext(); - while (hasNext) { - final K key = it.next(); - final V value = it.getValue(); - buf.append(key == this ? "(this Map)" : key) - .append('=') - .append(value == this ? "(this Map)" : value); - - hasNext = it.hasNext(); - if (hasNext) { - buf.append(',').append(' '); - } - } - - buf.append('}'); - return buf.toString(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * An abstract implementation of a hash-based map which provides numerous points for + * subclasses to override. + *

+ * This class implements all the features necessary for a subclass hash-based map. + * Key-value entries are stored in instances of the HashEntry class, + * which can be overridden and replaced. The iterators can similarly be replaced, + * without the need to replace the KeySet, EntrySet and Values view classes. + *

+ * Overridable methods are provided to change the default hashing behaviour, and + * to change how entries are added to and removed from the map. Hopefully, all you + * need for unusual subclasses is here. + *

+ * NOTE: From Commons Collections 3.1 this class extends AbstractMap. + * This is to provide backwards compatibility for ReferenceMap between v3.0 and v3.1. + * This extends clause will be removed in v5.0. + * + * @param the type of the keys in this map + * @param the type of the values in this map + * @since 3.0 + */ +public class AbstractHashedMap extends AbstractMap implements IterableMap { + + protected static final String NO_NEXT_ENTRY = "No next() entry in the iteration"; + protected static final String NO_PREVIOUS_ENTRY = "No previous() entry in the iteration"; + protected static final String REMOVE_INVALID = "remove() can only be called once after next()"; + protected static final String GETKEY_INVALID = "getKey() can only be called after next() and before remove()"; + protected static final String GETVALUE_INVALID = "getValue() can only be called after next() and before remove()"; + protected static final String SETVALUE_INVALID = "setValue() can only be called after next() and before remove()"; + + /** The default capacity to use */ + protected static final int DEFAULT_CAPACITY = 16; + /** The default threshold to use */ + protected static final int DEFAULT_THRESHOLD = 12; + /** The default load factor to use */ + protected static final float DEFAULT_LOAD_FACTOR = 0.75f; + /** The maximum capacity allowed */ + protected static final int MAXIMUM_CAPACITY = 1 << 30; + /** An object for masking null */ + protected static final Object NULL = new Object(); + + /** Load factor, normally 0.75 */ + transient float loadFactor; + /** The size of the map */ + transient int size; + /** Map entries */ + transient HashEntry[] data; + /** Size at which to rehash */ + transient int threshold; + /** Modification count for iterators */ + transient int modCount; + /** Entry set */ + transient EntrySet entrySet; + /** Key set */ + transient KeySet keySet; + /** Values */ + transient Values values; + + /** + * Constructor only used in deserialization, do not use otherwise. + */ + protected AbstractHashedMap() { + super(); + } + + /** + * Constructor which performs no validation on the passed in parameters. + * + * @param initialCapacity the initial capacity, must be a power of two + * @param loadFactor the load factor, must be > 0.0f and generally < 1.0f + * @param threshold the threshold, must be sensible + */ + @SuppressWarnings("unchecked") + protected AbstractHashedMap(final int initialCapacity, final float loadFactor, final int threshold) { + super(); + this.loadFactor = loadFactor; + this.data = new HashEntry[initialCapacity]; + this.threshold = threshold; + init(); + } + + /** + * Constructs a new, empty map with the specified initial capacity and + * default load factor. + * + * @param initialCapacity the initial capacity + * @throws IllegalArgumentException if the initial capacity is negative + */ + protected AbstractHashedMap(final int initialCapacity) { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Constructs a new, empty map with the specified initial capacity and + * load factor. + * + * @param initialCapacity the initial capacity + * @param loadFactor the load factor + * @throws IllegalArgumentException if the initial capacity is negative + * @throws IllegalArgumentException if the load factor is less than or equal to zero + */ + @SuppressWarnings("unchecked") + protected AbstractHashedMap(int initialCapacity, final float loadFactor) { + super(); + if (initialCapacity < 0) { + throw new IllegalArgumentException("Initial capacity must be a non negative number"); + } + if (loadFactor <= 0.0f || Float.isNaN(loadFactor)) { + throw new IllegalArgumentException("Load factor must be greater than 0"); + } + this.loadFactor = loadFactor; + initialCapacity = calculateNewCapacity(initialCapacity); + this.threshold = calculateThreshold(initialCapacity, loadFactor); + this.data = new HashEntry[initialCapacity]; + init(); + } + + /** + * Constructor copying elements from another map. + * + * @param map the map to copy + * @throws NullPointerException if the map is null + */ + protected AbstractHashedMap(final Map map) { + this(Math.max(2 * map.size(), DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); + _putAll(map); + } + + /** + * Initialise subclasses during construction, cloning or deserialization. + */ + protected void init() { + } + + //----------------------------------------------------------------------- + /** + * Gets the value mapped to the key specified. + * + * @param key the key + * @return the mapped value, null if no match + */ + @Override + public V get(Object key) { + key = convertKey(key); + final int hashCode = hash(key); + HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { + return entry.getValue(); + } + entry = entry.next; + } + return null; + } + + /** + * Gets the size of the map. + * + * @return the size + */ + @Override + public int size() { + return size; + } + + /** + * Checks whether the map is currently empty. + * + * @return true if the map is currently size zero + */ + @Override + public boolean isEmpty() { + return size == 0; + } + + //----------------------------------------------------------------------- + /** + * Checks whether the map contains the specified key. + * + * @param key the key to search for + * @return true if the map contains the key + */ + @Override + public boolean containsKey(Object key) { + key = convertKey(key); + final int hashCode = hash(key); + HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { + return true; + } + entry = entry.next; + } + return false; + } + + /** + * Checks whether the map contains the specified value. + * + * @param value the value to search for + * @return true if the map contains the value + */ + @Override + public boolean containsValue(final Object value) { + if (value == null) { + for (final HashEntry element : data) { + HashEntry entry = element; + while (entry != null) { + if (entry.getValue() == null) { + return true; + } + entry = entry.next; + } + } + } else { + for (final HashEntry element : data) { + HashEntry entry = element; + while (entry != null) { + if (isEqualValue(value, entry.getValue())) { + return true; + } + entry = entry.next; + } + } + } + return false; + } + + //----------------------------------------------------------------------- + /** + * Puts a key-value mapping into this map. + * + * @param key the key to add + * @param value the value to add + * @return the value previously mapped to this key, null if none + */ + @Override + public V put(final K key, final V value) { + final Object convertedKey = convertKey(key); + final int hashCode = hash(convertedKey); + final int index = hashIndex(hashCode, data.length); + HashEntry entry = data[index]; + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(convertedKey, entry.key)) { + final V oldValue = entry.getValue(); + updateEntry(entry, value); + return oldValue; + } + entry = entry.next; + } + + addMapping(index, hashCode, key, value); + return null; + } + + /** + * Puts all the values from the specified map into this map. + *

+ * This implementation iterates around the specified map and + * uses {@link #put(Object, Object)}. + * + * @param map the map to add + * @throws NullPointerException if the map is null + */ + @Override + public void putAll(final Map map) { + _putAll(map); + } + + /** + * Puts all the values from the specified map into this map. + *

+ * This implementation iterates around the specified map and + * uses {@link #put(Object, Object)}. + *

+ * It is private to allow the constructor to still call it + * even when putAll is overriden. + * + * @param map the map to add + * @throws NullPointerException if the map is null + */ + private void _putAll(final Map map) { + final int mapSize = map.size(); + if (mapSize == 0) { + return; + } + final int newSize = (int) ((size + mapSize) / loadFactor + 1); + ensureCapacity(calculateNewCapacity(newSize)); + for (final Entry entry: map.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + /** + * Removes the specified mapping from this map. + * + * @param key the mapping to remove + * @return the value mapped to the removed key, null if key not in map + */ + @Override + public V remove(Object key) { + key = convertKey(key); + final int hashCode = hash(key); + final int index = hashIndex(hashCode, data.length); + HashEntry entry = data[index]; + HashEntry previous = null; + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { + final V oldValue = entry.getValue(); + removeMapping(entry, index, previous); + return oldValue; + } + previous = entry; + entry = entry.next; + } + return null; + } + + /** + * Clears the map, resetting the size to zero and nullifying references + * to avoid garbage collection issues. + */ + @Override + public void clear() { + modCount++; + final HashEntry[] data = this.data; + for (int i = data.length - 1; i >= 0; i--) { + data[i] = null; + } + size = 0; + } + + //----------------------------------------------------------------------- + /** + * Converts input keys to another object for storage in the map. + * This implementation masks nulls. + * Subclasses can override this to perform alternate key conversions. + *

+ * The reverse conversion can be changed, if required, by overriding the + * getKey() method in the hash entry. + * + * @param key the key convert + * @return the converted key + */ + protected Object convertKey(final Object key) { + return key == null ? NULL : key; + } + + /** + * Gets the hash code for the key specified. + * This implementation uses the additional hashing routine from JDK1.4. + * Subclasses can override this to return alternate hash codes. + * + * @param key the key to get a hash code for + * @return the hash code + */ + protected int hash(final Object key) { + // same as JDK 1.4 + int h = key.hashCode(); + h += ~(h << 9); + h ^= h >>> 14; + h += h << 4; + h ^= h >>> 10; + return h; + } + + /** + * Compares two keys, in internal converted form, to see if they are equal. + * This implementation uses the equals method and assumes neither key is null. + * Subclasses can override this to match differently. + * + * @param key1 the first key to compare passed in from outside + * @param key2 the second key extracted from the entry via entry.key + * @return true if equal + */ + protected boolean isEqualKey(final Object key1, final Object key2) { + return key1 == key2 || key1.equals(key2); + } + + /** + * Compares two values, in external form, to see if they are equal. + * This implementation uses the equals method and assumes neither value is null. + * Subclasses can override this to match differently. + * + * @param value1 the first value to compare passed in from outside + * @param value2 the second value extracted from the entry via getValue() + * @return true if equal + */ + protected boolean isEqualValue(final Object value1, final Object value2) { + return value1 == value2 || value1.equals(value2); + } + + /** + * Gets the index into the data storage for the hashCode specified. + * This implementation uses the least significant bits of the hashCode. + * Subclasses can override this to return alternate bucketing. + * + * @param hashCode the hash code to use + * @param dataSize the size of the data to pick a bucket from + * @return the bucket index + */ + protected int hashIndex(final int hashCode, final int dataSize) { + return hashCode & dataSize - 1; + } + + //----------------------------------------------------------------------- + /** + * Gets the entry mapped to the key specified. + *

+ * This method exists for subclasses that may need to perform a multi-step + * process accessing the entry. The public methods in this class don't use this + * method to gain a small performance boost. + * + * @param key the key + * @return the entry, null if no match + */ + protected HashEntry getEntry(Object key) { + key = convertKey(key); + final int hashCode = hash(key); + HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { + return entry; + } + entry = entry.next; + } + return null; + } + + //----------------------------------------------------------------------- + /** + * Updates an existing key-value mapping to change the value. + *

+ * This implementation calls setValue() on the entry. + * Subclasses could override to handle changes to the map. + * + * @param entry the entry to update + * @param newValue the new value to store + */ + protected void updateEntry(final HashEntry entry, final V newValue) { + entry.setValue(newValue); + } + + /** + * Reuses an existing key-value mapping, storing completely new data. + *

+ * This implementation sets all the data fields on the entry. + * Subclasses could populate additional entry fields. + * + * @param entry the entry to update, not null + * @param hashIndex the index in the data array + * @param hashCode the hash code of the key to add + * @param key the key to add + * @param value the value to add + */ + protected void reuseEntry(final HashEntry entry, final int hashIndex, final int hashCode, + final K key, final V value) { + entry.next = data[hashIndex]; + entry.hashCode = hashCode; + entry.key = key; + entry.value = value; + } + + //----------------------------------------------------------------------- + /** + * Adds a new key-value mapping into this map. + *

+ * This implementation calls createEntry(), addEntry() + * and checkCapacity(). + * It also handles changes to modCount and size. + * Subclasses could override to fully control adds to the map. + * + * @param hashIndex the index into the data array to store at + * @param hashCode the hash code of the key to add + * @param key the key to add + * @param value the value to add + */ + protected void addMapping(final int hashIndex, final int hashCode, final K key, final V value) { + modCount++; + final HashEntry entry = createEntry(data[hashIndex], hashCode, key, value); + addEntry(entry, hashIndex); + size++; + checkCapacity(); + } + + /** + * Creates an entry to store the key-value data. + *

+ * This implementation creates a new HashEntry instance. + * Subclasses can override this to return a different storage class, + * or implement caching. + * + * @param next the next entry in sequence + * @param hashCode the hash code to use + * @param key the key to store + * @param value the value to store + * @return the newly created entry + */ + protected HashEntry createEntry(final HashEntry next, final int hashCode, final K key, final V value) { + return new HashEntry<>(next, hashCode, convertKey(key), value); + } + + /** + * Adds an entry into this map. + *

+ * This implementation adds the entry to the data storage table. + * Subclasses could override to handle changes to the map. + * + * @param entry the entry to add + * @param hashIndex the index into the data array to store at + */ + protected void addEntry(final HashEntry entry, final int hashIndex) { + data[hashIndex] = entry; + } + + //----------------------------------------------------------------------- + /** + * Removes a mapping from the map. + *

+ * This implementation calls removeEntry() and destroyEntry(). + * It also handles changes to modCount and size. + * Subclasses could override to fully control removals from the map. + * + * @param entry the entry to remove + * @param hashIndex the index into the data structure + * @param previous the previous entry in the chain + */ + protected void removeMapping(final HashEntry entry, final int hashIndex, final HashEntry previous) { + modCount++; + removeEntry(entry, hashIndex, previous); + size--; + destroyEntry(entry); + } + + /** + * Removes an entry from the chain stored in a particular index. + *

+ * This implementation removes the entry from the data storage table. + * The size is not updated. + * Subclasses could override to handle changes to the map. + * + * @param entry the entry to remove + * @param hashIndex the index into the data structure + * @param previous the previous entry in the chain + */ + protected void removeEntry(final HashEntry entry, final int hashIndex, final HashEntry previous) { + if (previous == null) { + data[hashIndex] = entry.next; + } else { + previous.next = entry.next; + } + } + + /** + * Kills an entry ready for the garbage collector. + *

+ * This implementation prepares the HashEntry for garbage collection. + * Subclasses can override this to implement caching (override clear as well). + * + * @param entry the entry to destroy + */ + protected void destroyEntry(final HashEntry entry) { + entry.next = null; + entry.key = null; + entry.value = null; + } + + //----------------------------------------------------------------------- + /** + * Checks the capacity of the map and enlarges it if necessary. + *

+ * This implementation uses the threshold to check if the map needs enlarging + */ + protected void checkCapacity() { + if (size >= threshold) { + final int newCapacity = data.length * 2; + if (newCapacity <= MAXIMUM_CAPACITY) { + ensureCapacity(newCapacity); + } + } + } + + /** + * Changes the size of the data structure to the capacity proposed. + * + * @param newCapacity the new capacity of the array (a power of two, less or equal to max) + */ + @SuppressWarnings("unchecked") + protected void ensureCapacity(final int newCapacity) { + final int oldCapacity = data.length; + if (newCapacity <= oldCapacity) { + return; + } + if (size == 0) { + threshold = calculateThreshold(newCapacity, loadFactor); + data = new HashEntry[newCapacity]; + } else { + final HashEntry oldEntries[] = data; + final HashEntry newEntries[] = new HashEntry[newCapacity]; + + modCount++; + for (int i = oldCapacity - 1; i >= 0; i--) { + HashEntry entry = oldEntries[i]; + if (entry != null) { + oldEntries[i] = null; // gc + do { + final HashEntry next = entry.next; + final int index = hashIndex(entry.hashCode, newCapacity); + entry.next = newEntries[index]; + newEntries[index] = entry; + entry = next; + } while (entry != null); + } + } + threshold = calculateThreshold(newCapacity, loadFactor); + data = newEntries; + } + } + + /** + * Calculates the new capacity of the map. + * This implementation normalizes the capacity to a power of two. + * + * @param proposedCapacity the proposed capacity + * @return the normalized new capacity + */ + protected int calculateNewCapacity(final int proposedCapacity) { + int newCapacity = 1; + if (proposedCapacity > MAXIMUM_CAPACITY) { + newCapacity = MAXIMUM_CAPACITY; + } else { + while (newCapacity < proposedCapacity) { + newCapacity <<= 1; // multiply by two + } + if (newCapacity > MAXIMUM_CAPACITY) { + newCapacity = MAXIMUM_CAPACITY; + } + } + return newCapacity; + } + + /** + * Calculates the new threshold of the map, where it will be resized. + * This implementation uses the load factor. + * + * @param newCapacity the new capacity + * @param factor the load factor + * @return the new resize threshold + */ + protected int calculateThreshold(final int newCapacity, final float factor) { + return (int) (newCapacity * factor); + } + + //----------------------------------------------------------------------- + /** + * Gets the next field from a HashEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the next field of the entry + * @throws NullPointerException if the entry is null + * @since 3.1 + */ + protected HashEntry entryNext(final HashEntry entry) { + return entry.next; + } + + /** + * Gets the hashCode field from a HashEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the hashCode field of the entry + * @throws NullPointerException if the entry is null + * @since 3.1 + */ + protected int entryHashCode(final HashEntry entry) { + return entry.hashCode; + } + + /** + * Gets the key field from a HashEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the key field of the entry + * @throws NullPointerException if the entry is null + * @since 3.1 + */ + protected K entryKey(final HashEntry entry) { + return entry.getKey(); + } + + /** + * Gets the value field from a HashEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the value field of the entry + * @throws NullPointerException if the entry is null + * @since 3.1 + */ + protected V entryValue(final HashEntry entry) { + return entry.getValue(); + } + + //----------------------------------------------------------------------- + /** + * Gets an iterator over the map. + * Changes made to the iterator affect this map. + *

+ * A MapIterator returns the keys in the map. It also provides convenient + * methods to get the key and value, and set the value. + * It avoids the need to create an entrySet/keySet/values object. + * It also avoids creating the Map.Entry object. + * + * @return the map iterator + */ + @Override + public MapIterator mapIterator() { + if (size == 0) { + return EmptyMapIterator.emptyMapIterator(); + } + return new HashMapIterator<>(this); + } + + /** + * MapIterator implementation. + */ + protected static class HashMapIterator extends HashIterator implements MapIterator { + + protected HashMapIterator(final AbstractHashedMap parent) { + super(parent); + } + + @Override + public K next() { + return super.nextEntry().getKey(); + } + + @Override + public K getKey() { + final HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); + } + return current.getKey(); + } + + @Override + public V getValue() { + final HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); + } + return current.getValue(); + } + + @Override + public V setValue(final V value) { + final HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); + } + return current.setValue(value); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the entrySet view of the map. + * Changes made to the view affect this map. + * To simply iterate through the entries, use {@link #mapIterator()}. + * + * @return the entrySet view + */ + @Override + public Set> entrySet() { + if (entrySet == null) { + entrySet = new EntrySet<>(this); + } + return entrySet; + } + + /** + * Creates an entry set iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the entrySet iterator + */ + protected Iterator> createEntrySetIterator() { + if (size() == 0) { + return EmptyIterator.>emptyIterator(); + } + return new EntrySetIterator<>(this); + } + + /** + * EntrySet implementation. + */ + protected static class EntrySet extends AbstractSet> { + /** The parent map */ + private final AbstractHashedMap parent; + + protected EntrySet(final AbstractHashedMap parent) { + super(); + this.parent = parent; + } + + @Override + public int size() { + return parent.size(); + } + + @Override + public void clear() { + parent.clear(); + } + + @Override + public boolean contains(final Object entry) { + if (entry instanceof Map.Entry) { + final Entry e = (Entry) entry; + final Entry match = parent.getEntry(e.getKey()); + return match != null && match.equals(e); + } + return false; + } + + @Override + public boolean remove(final Object obj) { + if (!(obj instanceof Map.Entry)) { + return false; + } + if (!contains(obj)) { + return false; + } + final Entry entry = (Entry) obj; + parent.remove(entry.getKey()); + return true; + } + + @Override + public Iterator> iterator() { + return parent.createEntrySetIterator(); + } + } + + /** + * EntrySet iterator. + */ + protected static class EntrySetIterator extends HashIterator implements Iterator> { + + protected EntrySetIterator(final AbstractHashedMap parent) { + super(parent); + } + + @Override + public Entry next() { + return super.nextEntry(); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the keySet view of the map. + * Changes made to the view affect this map. + * To simply iterate through the keys, use {@link #mapIterator()}. + * + * @return the keySet view + */ + @Override + public Set keySet() { + if (keySet == null) { + keySet = new KeySet<>(this); + } + return keySet; + } + + /** + * Creates a key set iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the keySet iterator + */ + protected Iterator createKeySetIterator() { + if (size() == 0) { + return EmptyIterator.emptyIterator(); + } + return new KeySetIterator<>(this); + } + + /** + * KeySet implementation. + */ + protected static class KeySet extends AbstractSet { + /** The parent map */ + private final AbstractHashedMap parent; + + protected KeySet(final AbstractHashedMap parent) { + super(); + this.parent = parent; + } + + @Override + public int size() { + return parent.size(); + } + + @Override + public void clear() { + parent.clear(); + } + + @Override + public boolean contains(final Object key) { + return parent.containsKey(key); + } + + @Override + public boolean remove(final Object key) { + final boolean result = parent.containsKey(key); + parent.remove(key); + return result; + } + + @Override + public Iterator iterator() { + return parent.createKeySetIterator(); + } + } + + /** + * KeySet iterator. + */ + protected static class KeySetIterator extends HashIterator implements Iterator { + + @SuppressWarnings("unchecked") + protected KeySetIterator(final AbstractHashedMap parent) { + super((AbstractHashedMap) parent); + } + + @Override + public K next() { + return super.nextEntry().getKey(); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the values view of the map. + * Changes made to the view affect this map. + * To simply iterate through the values, use {@link #mapIterator()}. + * + * @return the values view + */ + @Override + public Collection values() { + if (values == null) { + values = new Values<>(this); + } + return values; + } + + /** + * Creates a values iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the values iterator + */ + protected Iterator createValuesIterator() { + if (size() == 0) { + return EmptyIterator.emptyIterator(); + } + return new ValuesIterator<>(this); + } + + /** + * Values implementation. + */ + protected static class Values extends AbstractCollection { + /** The parent map */ + private final AbstractHashedMap parent; + + protected Values(final AbstractHashedMap parent) { + super(); + this.parent = parent; + } + + @Override + public int size() { + return parent.size(); + } + + @Override + public void clear() { + parent.clear(); + } + + @Override + public boolean contains(final Object value) { + return parent.containsValue(value); + } + + @Override + public Iterator iterator() { + return parent.createValuesIterator(); + } + } + + /** + * Values iterator. + */ + protected static class ValuesIterator extends HashIterator implements Iterator { + + @SuppressWarnings("unchecked") + protected ValuesIterator(final AbstractHashedMap parent) { + super((AbstractHashedMap) parent); + } + + @Override + public V next() { + return super.nextEntry().getValue(); + } + } + + //----------------------------------------------------------------------- + /** + * HashEntry used to store the data. + *

+ * If you subclass AbstractHashedMap but not HashEntry + * then you will not be able to access the protected fields. + * The entryXxx() methods on AbstractHashedMap exist + * to provide the necessary access. + */ + protected static class HashEntry implements Entry, KeyValue { + /** The next entry in the hash chain */ + protected HashEntry next; + /** The hash code of the key */ + protected int hashCode; + /** The key */ + protected Object key; + /** The value */ + protected Object value; + + protected HashEntry(final HashEntry next, final int hashCode, final Object key, final V value) { + super(); + this.next = next; + this.hashCode = hashCode; + this.key = key; + this.value = value; + } + + @Override + @SuppressWarnings("unchecked") + public K getKey() { + if (key == NULL) { + return null; + } + return (K) key; + } + + @Override + @SuppressWarnings("unchecked") + public V getValue() { + return (V) value; + } + + @Override + @SuppressWarnings("unchecked") + public V setValue(final V value) { + final Object old = this.value; + this.value = value; + return (V) old; + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Map.Entry)) { + return false; + } + final Entry other = (Entry) obj; + return + (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) && + (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())); + } + + @Override + public int hashCode() { + return (getKey() == null ? 0 : getKey().hashCode()) ^ + (getValue() == null ? 0 : getValue().hashCode()); + } + + @Override + public String toString() { + return new StringBuilder().append(getKey()).append('=').append(getValue()).toString(); + } + } + + /** + * Base Iterator + */ + protected static abstract class HashIterator { + + /** The parent map */ + private final AbstractHashedMap parent; + /** The current index into the array of buckets */ + private int hashIndex; + /** The last returned entry */ + private HashEntry last; + /** The next entry */ + private HashEntry next; + /** The modification count expected */ + private int expectedModCount; + + protected HashIterator(final AbstractHashedMap parent) { + super(); + this.parent = parent; + final HashEntry[] data = parent.data; + int i = data.length; + HashEntry next = null; + while (i > 0 && next == null) { + next = data[--i]; + } + this.next = next; + this.hashIndex = i; + this.expectedModCount = parent.modCount; + } + + public boolean hasNext() { + return next != null; + } + + protected HashEntry nextEntry() { + if (parent.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + final HashEntry newCurrent = next; + if (newCurrent == null) { + throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY); + } + final HashEntry[] data = parent.data; + int i = hashIndex; + HashEntry n = newCurrent.next; + while (n == null && i > 0) { + n = data[--i]; + } + next = n; + hashIndex = i; + last = newCurrent; + return newCurrent; + } + + protected HashEntry currentEntry() { + return last; + } + + public void remove() { + if (last == null) { + throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID); + } + if (parent.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + parent.remove(last.getKey()); + last = null; + expectedModCount = parent.modCount; + } + + @Override + public String toString() { + if (last != null) { + return "Iterator[" + last.getKey() + "=" + last.getValue() + "]"; + } + return "Iterator[]"; + } + } + + //----------------------------------------------------------------------- + /** + * Writes the map data to the stream. This method must be overridden if a + * subclass must be setup before put() is used. + *

+ * Serialization is not one of the JDK's nicest topics. Normal serialization will + * initialise the superclass before the subclass. Sometimes however, this isn't + * what you want, as in this case the put() method on read can be + * affected by subclass state. + *

+ * The solution adopted here is to serialize the state data of this class in + * this protected method. This method must be called by the + * writeObject() of the first serializable subclass. + *

+ * Subclasses may override if they have a specific field that must be present + * on read before this implementation will work. Generally, the read determines + * what must be serialized here, if anything. + * + * @param out the output stream + * @throws IOException if an error occurs while writing tothe stream + */ + protected void doWriteObject(final ObjectOutputStream out) throws IOException { + out.writeFloat(loadFactor); + out.writeInt(data.length); + out.writeInt(size); + for (final MapIterator it = mapIterator(); it.hasNext();) { + out.writeObject(it.next()); + out.writeObject(it.getValue()); + } + } + + /** + * Reads the map data from the stream. This method must be overridden if a + * subclass must be setup before put() is used. + *

+ * Serialization is not one of the JDK's nicest topics. Normal serialization will + * initialise the superclass before the subclass. Sometimes however, this isn't + * what you want, as in this case the put() method on read can be + * affected by subclass state. + *

+ * The solution adopted here is to deserialize the state data of this class in + * this protected method. This method must be called by the + * readObject() of the first serializable subclass. + *

+ * Subclasses may override if the subclass has a specific field that must be present + * before put() or calculateThreshold() will work correctly. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + @SuppressWarnings("unchecked") + protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + loadFactor = in.readFloat(); + final int capacity = in.readInt(); + final int size = in.readInt(); + init(); + threshold = calculateThreshold(capacity, loadFactor); + data = new HashEntry[capacity]; + for (int i = 0; i < size; i++) { + final K key = (K) in.readObject(); + final V value = (V) in.readObject(); + put(key, value); + } + } + + //----------------------------------------------------------------------- + /** + * Clones the map without cloning the keys or values. + *

+ * To implement clone(), a subclass must implement the + * Cloneable interface and make this method public. + * + * @return a shallow clone + * @throws InternalError if {@link AbstractMap#clone()} failed + */ + @Override + @SuppressWarnings("unchecked") + protected AbstractHashedMap clone() { + try { + final AbstractHashedMap cloned = (AbstractHashedMap) super.clone(); + cloned.data = new HashEntry[data.length]; + cloned.entrySet = null; + cloned.keySet = null; + cloned.values = null; + cloned.modCount = 0; + cloned.size = 0; + cloned.init(); + cloned.putAll(this); + return cloned; + } catch (final CloneNotSupportedException ex) { + throw new InternalError(); + } + } + + /** + * Compares this map with another. + * + * @param obj the object to compare to + * @return true if equal + */ + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Map)) { + return false; + } + final Map map = (Map) obj; + if (map.size() != size()) { + return false; + } + final MapIterator it = mapIterator(); + try { + while (it.hasNext()) { + final Object key = it.next(); + final Object value = it.getValue(); + if (value == null) { + if (map.get(key) != null || !map.containsKey(key)) { + return false; + } + } else { + if (!value.equals(map.get(key))) { + return false; + } + } + } + } catch (final ClassCastException | NullPointerException ignored) { + return false; + } + return true; + } + + /** + * Gets the standard Map hashCode. + * + * @return the hash code defined in the Map interface + */ + @Override + public int hashCode() { + int total = 0; + final Iterator> it = createEntrySetIterator(); + while (it.hasNext()) { + total += it.next().hashCode(); + } + return total; + } + + /** + * Gets the map as a String. + * + * @return a string version of the map + */ + @Override + public String toString() { + if (size() == 0) { + return "{}"; + } + final StringBuilder buf = new StringBuilder(32 * size()); + buf.append('{'); + + final MapIterator it = mapIterator(); + boolean hasNext = it.hasNext(); + while (hasNext) { + final K key = it.next(); + final V value = it.getValue(); + buf.append(key == this ? "(this Map)" : key) + .append('=') + .append(value == this ? "(this Map)" : value); + + hasNext = it.hasNext(); + if (hasNext) { + buf.append(',').append(' '); + } + } + + buf.append('}'); + return buf.toString(); + } +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractIterableMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractIterableMap.java index ff3d1ca291..51acc744b0 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractIterableMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractIterableMap.java @@ -1,35 +1,35 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Provide a basic {@link IterableMap} implementation. - * - * @param the type of the keys in this map - * @param the type of the values in this map - * @since 4.0 - */ -public abstract class AbstractIterableMap implements IterableMap { - - /** - * {@inheritDoc} - */ - @Override - public MapIterator mapIterator() { - return new EntrySetToMapIteratorAdapter<>(entrySet()); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Provide a basic {@link IterableMap} implementation. + * + * @param the type of the keys in this map + * @param the type of the values in this map + * @since 4.0 + */ +public abstract class AbstractIterableMap implements IterableMap { + + /** + * {@inheritDoc} + */ + @Override + public MapIterator mapIterator() { + return new EntrySetToMapIteratorAdapter<>(entrySet()); + } +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractIteratorDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractIteratorDecorator.java index 06309e1922..3d73d8c6e9 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractIteratorDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractIteratorDecorator.java @@ -1,47 +1,47 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Iterator; - -/** - * Provides basic behaviour for decorating an iterator with extra functionality. - *

- * All methods are forwarded to the decorated iterator. - * - * @since 3.0 - */ -public abstract class AbstractIteratorDecorator extends AbstractUntypedIteratorDecorator { - - //----------------------------------------------------------------------- - /** - * Constructor that decorates the specified iterator. - * - * @param iterator the iterator to decorate, must not be null - * @throws NullPointerException if the iterator is null - */ - protected AbstractIteratorDecorator(final Iterator iterator) { - super(iterator); - } - - /** {@inheritDoc} */ - @Override - public E next() { - return getIterator().next(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Iterator; + +/** + * Provides basic behaviour for decorating an iterator with extra functionality. + *

+ * All methods are forwarded to the decorated iterator. + * + * @since 3.0 + */ +public abstract class AbstractIteratorDecorator extends AbstractUntypedIteratorDecorator { + + //----------------------------------------------------------------------- + /** + * Constructor that decorates the specified iterator. + * + * @param iterator the iterator to decorate, must not be null + * @throws NullPointerException if the iterator is null + */ + protected AbstractIteratorDecorator(final Iterator iterator) { + super(iterator); + } + + /** {@inheritDoc} */ + @Override + public E next() { + return getIterator().next(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractKeyValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractKeyValue.java index 0ce80200ea..a545407648 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractKeyValue.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractKeyValue.java @@ -1,92 +1,92 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Abstract pair class to assist with creating KeyValue - * and {@link java.util.Map.Entry Map.Entry} implementations. - * - * @param the type of keys - * @param the type of values - * @since 3.0 - */ -public abstract class AbstractKeyValue implements KeyValue { - - /** The key */ - private K key; - /** The value */ - private V value; - - /** - * Constructs a new pair with the specified key and given value. - * - * @param key the key for the entry, may be null - * @param value the value for the entry, may be null - */ - protected AbstractKeyValue(final K key, final V value) { - super(); - this.key = key; - this.value = value; - } - - /** - * Gets the key from the pair. - * - * @return the key - */ - @Override - public K getKey() { - return key; - } - - protected K setKey(final K key) { - final K old = this.key; - this.key = key; - return old; - } - - /** - * Gets the value from the pair. - * - * @return the value - */ - @Override - public V getValue() { - return value; - } - - protected V setValue(final V value) { - final V old = this.value; - this.value = value; - return old; - } - - /** - * Gets a debugging String view of the pair. - * - * @return a String view of the entry - */ - @Override - public String toString() { - return new StringBuilder() - .append(getKey()) - .append('=') - .append(getValue()) - .toString(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Abstract pair class to assist with creating KeyValue + * and {@link java.util.Map.Entry Map.Entry} implementations. + * + * @param the type of keys + * @param the type of values + * @since 3.0 + */ +public abstract class AbstractKeyValue implements KeyValue { + + /** The key */ + private K key; + /** The value */ + private V value; + + /** + * Constructs a new pair with the specified key and given value. + * + * @param key the key for the entry, may be null + * @param value the value for the entry, may be null + */ + protected AbstractKeyValue(final K key, final V value) { + super(); + this.key = key; + this.value = value; + } + + /** + * Gets the key from the pair. + * + * @return the key + */ + @Override + public K getKey() { + return key; + } + + protected K setKey(final K key) { + final K old = this.key; + this.key = key; + return old; + } + + /** + * Gets the value from the pair. + * + * @return the value + */ + @Override + public V getValue() { + return value; + } + + protected V setValue(final V value) { + final V old = this.value; + this.value = value; + return old; + } + + /** + * Gets a debugging String view of the pair. + * + * @return a String view of the entry + */ + @Override + public String toString() { + return new StringBuilder() + .append(getKey()) + .append('=') + .append(getValue()) + .toString(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractLinkedMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractLinkedMap.java index adf161baae..343a4fc280 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractLinkedMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractLinkedMap.java @@ -1,620 +1,620 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; - -/** - * An abstract implementation of a hash-based map that links entries to create an - * ordered map and which provides numerous points for subclasses to override. - *

- * This class implements all the features necessary for a subclass linked - * hash-based map. Key-value entries are stored in instances of the - * LinkEntry class which can be overridden and replaced. - * The iterators can similarly be replaced, without the need to replace the KeySet, - * EntrySet and Values view classes. - *

- *

- * Overridable methods are provided to change the default hashing behaviour, and - * to change how entries are added to and removed from the map. Hopefully, all you - * need for unusual subclasses is here. - *

- *

- * This implementation maintains order by original insertion, but subclasses - * may work differently. The OrderedMap interface is implemented - * to provide access to bidirectional iteration and extra convenience methods. - *

- *

- * The orderedMapIterator() method provides direct access to a - * bidirectional iterator. The iterators from the other views can also be cast - * to OrderedIterator if required. - *

- *

- * All the available iterators can be reset back to the start by casting to - * ResettableIterator and calling reset(). - *

- *

- * The implementation is also designed to be subclassed, with lots of useful - * methods exposed. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * @since 3.0 - */ -public abstract class AbstractLinkedMap extends AbstractHashedMap implements OrderedMap { - - /** Header in the linked list */ - transient LinkEntry header; - - /** - * Constructor only used in deserialization, do not use otherwise. - */ - protected AbstractLinkedMap() { - super(); - } - - /** - * Constructor which performs no validation on the passed in parameters. - * - * @param initialCapacity the initial capacity, must be a power of two - * @param loadFactor the load factor, must be > 0.0f and generally < 1.0f - * @param threshold the threshold, must be sensible - */ - protected AbstractLinkedMap(final int initialCapacity, final float loadFactor, final int threshold) { - super(initialCapacity, loadFactor, threshold); - } - - /** - * Constructs a new, empty map with the specified initial capacity. - * - * @param initialCapacity the initial capacity - * @throws IllegalArgumentException if the initial capacity is negative - */ - protected AbstractLinkedMap(final int initialCapacity) { - super(initialCapacity); - } - - /** - * Constructs a new, empty map with the specified initial capacity and - * load factor. - * - * @param initialCapacity the initial capacity - * @param loadFactor the load factor - * @throws IllegalArgumentException if the initial capacity is negative - * @throws IllegalArgumentException if the load factor is less than zero - */ - protected AbstractLinkedMap(final int initialCapacity, final float loadFactor) { - super(initialCapacity, loadFactor); - } - - /** - * Constructor copying elements from another map. - * - * @param map the map to copy - * @throws NullPointerException if the map is null - */ - protected AbstractLinkedMap(final Map map) { - super(map); - } - - /** - * Initialise this subclass during construction. - *

- * NOTE: As from v3.2 this method calls - * {@link #createEntry(HashEntry, int, Object, Object)} to create - * the map entry object. - */ - @Override - protected void init() { - header = createEntry(null, -1, null, null); - header.before = header.after = header; - } - - //----------------------------------------------------------------------- - /** - * Checks whether the map contains the specified value. - * - * @param value the value to search for - * @return true if the map contains the value - */ - @Override - public boolean containsValue(final Object value) { - // override uses faster iterator - if (value == null) { - for (LinkEntry entry = header.after; entry != header; entry = entry.after) { - if (entry.getValue() == null) { - return true; - } - } - } else { - for (LinkEntry entry = header.after; entry != header; entry = entry.after) { - if (isEqualValue(value, entry.getValue())) { - return true; - } - } - } - return false; - } - - /** - * Clears the map, resetting the size to zero and nullifying references - * to avoid garbage collection issues. - */ - @Override - public void clear() { - // override to reset the linked list - super.clear(); - header.before = header.after = header; - } - - //----------------------------------------------------------------------- - /** - * Gets the first key in the map, which is the first inserted. - * - * @return the eldest key - */ - @Override - public K firstKey() { - if (size == 0) { - throw new NoSuchElementException("Map is empty"); - } - return header.after.getKey(); - } - - /** - * Gets the last key in the map, which is the most recently inserted. - * - * @return the most recently inserted key - */ - @Override - public K lastKey() { - if (size == 0) { - throw new NoSuchElementException("Map is empty"); - } - return header.before.getKey(); - } - - /** - * Gets the next key in sequence. - * - * @param key the key to get after - * @return the next key - */ - @Override - public K nextKey(final Object key) { - final LinkEntry entry = getEntry(key); - return entry == null || entry.after == header ? null : entry.after.getKey(); - } - - @Override - protected LinkEntry getEntry(final Object key) { - return (LinkEntry) super.getEntry(key); - } - - /** - * Gets the previous key in sequence. - * - * @param key the key to get before - * @return the previous key - */ - @Override - public K previousKey(final Object key) { - final LinkEntry entry = getEntry(key); - return entry == null || entry.before == header ? null : entry.before.getKey(); - } - - //----------------------------------------------------------------------- - /** - * Gets the key at the specified index. - * - * @param index the index to retrieve - * @return the key at the specified index - * @throws IndexOutOfBoundsException if the index is invalid - */ - protected LinkEntry getEntry(final int index) { - if (index < 0) { - throw new IndexOutOfBoundsException("Index " + index + " is less than zero"); - } - if (index >= size) { - throw new IndexOutOfBoundsException("Index " + index + " is invalid for size " + size); - } - LinkEntry entry; - if (index < size / 2) { - // Search forwards - entry = header.after; - for (int currentIndex = 0; currentIndex < index; currentIndex++) { - entry = entry.after; - } - } else { - // Search backwards - entry = header; - for (int currentIndex = size; currentIndex > index; currentIndex--) { - entry = entry.before; - } - } - return entry; - } - - /** - * Adds an entry into this map, maintaining insertion order. - *

- * This implementation adds the entry to the data storage table and - * to the end of the linked list. - * - * @param entry the entry to add - * @param hashIndex the index into the data array to store at - */ - @Override - protected void addEntry(final HashEntry entry, final int hashIndex) { - final LinkEntry link = (LinkEntry) entry; - link.after = header; - link.before = header.before; - header.before.after = link; - header.before = link; - data[hashIndex] = link; - } - - /** - * Creates an entry to store the data. - *

- * This implementation creates a new LinkEntry instance. - * - * @param next the next entry in sequence - * @param hashCode the hash code to use - * @param key the key to store - * @param value the value to store - * @return the newly created entry - */ - @Override - protected LinkEntry createEntry(final HashEntry next, final int hashCode, final K key, final V value) { - return new LinkEntry<>(next, hashCode, convertKey(key), value); - } - - /** - * Removes an entry from the map and the linked list. - *

- * This implementation removes the entry from the linked list chain, then - * calls the superclass implementation. - * - * @param entry the entry to remove - * @param hashIndex the index into the data structure - * @param previous the previous entry in the chain - */ - @Override - protected void removeEntry(final HashEntry entry, final int hashIndex, final HashEntry previous) { - final LinkEntry link = (LinkEntry) entry; - link.before.after = link.after; - link.after.before = link.before; - link.after = null; - link.before = null; - super.removeEntry(entry, hashIndex, previous); - } - - //----------------------------------------------------------------------- - /** - * Gets the before field from a LinkEntry. - * Used in subclasses that have no visibility of the field. - * - * @param entry the entry to query, must not be null - * @return the before field of the entry - * @throws NullPointerException if the entry is null - * @since 3.1 - */ - protected LinkEntry entryBefore(final LinkEntry entry) { - return entry.before; - } - - /** - * Gets the after field from a LinkEntry. - * Used in subclasses that have no visibility of the field. - * - * @param entry the entry to query, must not be null - * @return the after field of the entry - * @throws NullPointerException if the entry is null - * @since 3.1 - */ - protected LinkEntry entryAfter(final LinkEntry entry) { - return entry.after; - } - - //----------------------------------------------------------------------- - /** - * {@inheritDoc} - */ - @Override - public OrderedMapIterator mapIterator() { - if (size == 0) { - return EmptyOrderedMapIterator.emptyOrderedMapIterator(); - } - return new LinkMapIterator<>(this); - } - - /** - * MapIterator implementation. - */ - protected static class LinkMapIterator extends LinkIterator implements - OrderedMapIterator, ResettableIterator { - - protected LinkMapIterator(final AbstractLinkedMap parent) { - super(parent); - } - - @Override - public K next() { - return super.nextEntry().getKey(); - } - - @Override - public K previous() { - return super.previousEntry().getKey(); - } - - @Override - public K getKey() { - final LinkEntry current = currentEntry(); - if (current == null) { - throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); - } - return current.getKey(); - } - - @Override - public V getValue() { - final LinkEntry current = currentEntry(); - if (current == null) { - throw new IllegalStateException(GETVALUE_INVALID); - } - return current.getValue(); - } - - @Override - public V setValue(final V value) { - final LinkEntry current = currentEntry(); - if (current == null) { - throw new IllegalStateException(SETVALUE_INVALID); - } - return current.setValue(value); - } - } - - //----------------------------------------------------------------------- - /** - * Creates an entry set iterator. - * Subclasses can override this to return iterators with different properties. - * - * @return the entrySet iterator - */ - @Override - protected Iterator> createEntrySetIterator() { - if (size() == 0) { - return EmptyOrderedIterator.>emptyOrderedIterator(); - } - return new EntrySetIterator<>(this); - } - - /** - * EntrySet iterator. - */ - protected static class EntrySetIterator extends LinkIterator implements - OrderedIterator>, ResettableIterator> { - - protected EntrySetIterator(final AbstractLinkedMap parent) { - super(parent); - } - - @Override - public Entry next() { - return super.nextEntry(); - } - - @Override - public Entry previous() { - return super.previousEntry(); - } - } - - //----------------------------------------------------------------------- - /** - * Creates a key set iterator. - * Subclasses can override this to return iterators with different properties. - * - * @return the keySet iterator - */ - @Override - protected Iterator createKeySetIterator() { - if (size() == 0) { - return EmptyOrderedIterator.emptyOrderedIterator(); - } - return new KeySetIterator<>(this); - } - - /** - * KeySet iterator. - */ - protected static class KeySetIterator extends LinkIterator implements - OrderedIterator, ResettableIterator { - - @SuppressWarnings("unchecked") - protected KeySetIterator(final AbstractLinkedMap parent) { - super((AbstractLinkedMap) parent); - } - - @Override - public K next() { - return super.nextEntry().getKey(); - } - - @Override - public K previous() { - return super.previousEntry().getKey(); - } - } - - //----------------------------------------------------------------------- - /** - * Creates a values iterator. - * Subclasses can override this to return iterators with different properties. - * - * @return the values iterator - */ - @Override - protected Iterator createValuesIterator() { - if (size() == 0) { - return EmptyOrderedIterator.emptyOrderedIterator(); - } - return new ValuesIterator<>(this); - } - - /** - * Values iterator. - */ - protected static class ValuesIterator extends LinkIterator implements - OrderedIterator, ResettableIterator { - - @SuppressWarnings("unchecked") - protected ValuesIterator(final AbstractLinkedMap parent) { - super((AbstractLinkedMap) parent); - } - - @Override - public V next() { - return super.nextEntry().getValue(); - } - - @Override - public V previous() { - return super.previousEntry().getValue(); - } - } - - //----------------------------------------------------------------------- - /** - * LinkEntry that stores the data. - *

- * If you subclass AbstractLinkedMap but not LinkEntry - * then you will not be able to access the protected fields. - * The entryXxx() methods on AbstractLinkedMap exist - * to provide the necessary access. - */ - protected static class LinkEntry extends HashEntry { - /** The entry before this one in the order */ - protected LinkEntry before; - /** The entry after this one in the order */ - protected LinkEntry after; - - /** - * Constructs a new entry. - * - * @param next the next entry in the hash bucket sequence - * @param hashCode the hash code - * @param key the key - * @param value the value - */ - protected LinkEntry(final HashEntry next, final int hashCode, final Object key, final V value) { - super(next, hashCode, key, value); - } - } - - /** - * Base Iterator that iterates in link order. - */ - protected static abstract class LinkIterator { - - /** The parent map */ - protected final AbstractLinkedMap parent; - /** The current (last returned) entry */ - protected LinkEntry last; - /** The next entry */ - protected LinkEntry next; - /** The modification count expected */ - protected int expectedModCount; - - protected LinkIterator(final AbstractLinkedMap parent) { - super(); - this.parent = parent; - this.next = parent.header.after; - this.expectedModCount = parent.modCount; - } - - public boolean hasNext() { - return next != parent.header; - } - - public boolean hasPrevious() { - return next.before != parent.header; - } - - protected LinkEntry nextEntry() { - if (parent.modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - if (next == parent.header) { - throw new NoSuchElementException(NO_NEXT_ENTRY); - } - last = next; - next = next.after; - return last; - } - - protected LinkEntry previousEntry() { - if (parent.modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - final LinkEntry previous = next.before; - if (previous == parent.header) { - throw new NoSuchElementException(AbstractHashedMap.NO_PREVIOUS_ENTRY); - } - next = previous; - last = previous; - return last; - } - - protected LinkEntry currentEntry() { - return last; - } - - public void remove() { - if (last == null) { - throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID); - } - if (parent.modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - parent.remove(last.getKey()); - last = null; - expectedModCount = parent.modCount; - } - - public void reset() { - last = null; - next = parent.header.after; - } - - @Override - public String toString() { - if (last != null) { - return "Iterator[" + last.getKey() + "=" + last.getValue() + "]"; - } - return "Iterator[]"; - } - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; + +/** + * An abstract implementation of a hash-based map that links entries to create an + * ordered map and which provides numerous points for subclasses to override. + *

+ * This class implements all the features necessary for a subclass linked + * hash-based map. Key-value entries are stored in instances of the + * LinkEntry class which can be overridden and replaced. + * The iterators can similarly be replaced, without the need to replace the KeySet, + * EntrySet and Values view classes. + *

+ *

+ * Overridable methods are provided to change the default hashing behaviour, and + * to change how entries are added to and removed from the map. Hopefully, all you + * need for unusual subclasses is here. + *

+ *

+ * This implementation maintains order by original insertion, but subclasses + * may work differently. The OrderedMap interface is implemented + * to provide access to bidirectional iteration and extra convenience methods. + *

+ *

+ * The orderedMapIterator() method provides direct access to a + * bidirectional iterator. The iterators from the other views can also be cast + * to OrderedIterator if required. + *

+ *

+ * All the available iterators can be reset back to the start by casting to + * ResettableIterator and calling reset(). + *

+ *

+ * The implementation is also designed to be subclassed, with lots of useful + * methods exposed. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * @since 3.0 + */ +public abstract class AbstractLinkedMap extends AbstractHashedMap implements OrderedMap { + + /** Header in the linked list */ + transient LinkEntry header; + + /** + * Constructor only used in deserialization, do not use otherwise. + */ + protected AbstractLinkedMap() { + super(); + } + + /** + * Constructor which performs no validation on the passed in parameters. + * + * @param initialCapacity the initial capacity, must be a power of two + * @param loadFactor the load factor, must be > 0.0f and generally < 1.0f + * @param threshold the threshold, must be sensible + */ + protected AbstractLinkedMap(final int initialCapacity, final float loadFactor, final int threshold) { + super(initialCapacity, loadFactor, threshold); + } + + /** + * Constructs a new, empty map with the specified initial capacity. + * + * @param initialCapacity the initial capacity + * @throws IllegalArgumentException if the initial capacity is negative + */ + protected AbstractLinkedMap(final int initialCapacity) { + super(initialCapacity); + } + + /** + * Constructs a new, empty map with the specified initial capacity and + * load factor. + * + * @param initialCapacity the initial capacity + * @param loadFactor the load factor + * @throws IllegalArgumentException if the initial capacity is negative + * @throws IllegalArgumentException if the load factor is less than zero + */ + protected AbstractLinkedMap(final int initialCapacity, final float loadFactor) { + super(initialCapacity, loadFactor); + } + + /** + * Constructor copying elements from another map. + * + * @param map the map to copy + * @throws NullPointerException if the map is null + */ + protected AbstractLinkedMap(final Map map) { + super(map); + } + + /** + * Initialise this subclass during construction. + *

+ * NOTE: As from v3.2 this method calls + * {@link #createEntry(HashEntry, int, Object, Object)} to create + * the map entry object. + */ + @Override + protected void init() { + header = createEntry(null, -1, null, null); + header.before = header.after = header; + } + + //----------------------------------------------------------------------- + /** + * Checks whether the map contains the specified value. + * + * @param value the value to search for + * @return true if the map contains the value + */ + @Override + public boolean containsValue(final Object value) { + // override uses faster iterator + if (value == null) { + for (LinkEntry entry = header.after; entry != header; entry = entry.after) { + if (entry.getValue() == null) { + return true; + } + } + } else { + for (LinkEntry entry = header.after; entry != header; entry = entry.after) { + if (isEqualValue(value, entry.getValue())) { + return true; + } + } + } + return false; + } + + /** + * Clears the map, resetting the size to zero and nullifying references + * to avoid garbage collection issues. + */ + @Override + public void clear() { + // override to reset the linked list + super.clear(); + header.before = header.after = header; + } + + //----------------------------------------------------------------------- + /** + * Gets the first key in the map, which is the first inserted. + * + * @return the eldest key + */ + @Override + public K firstKey() { + if (size == 0) { + throw new NoSuchElementException("Map is empty"); + } + return header.after.getKey(); + } + + /** + * Gets the last key in the map, which is the most recently inserted. + * + * @return the most recently inserted key + */ + @Override + public K lastKey() { + if (size == 0) { + throw new NoSuchElementException("Map is empty"); + } + return header.before.getKey(); + } + + /** + * Gets the next key in sequence. + * + * @param key the key to get after + * @return the next key + */ + @Override + public K nextKey(final Object key) { + final LinkEntry entry = getEntry(key); + return entry == null || entry.after == header ? null : entry.after.getKey(); + } + + @Override + protected LinkEntry getEntry(final Object key) { + return (LinkEntry) super.getEntry(key); + } + + /** + * Gets the previous key in sequence. + * + * @param key the key to get before + * @return the previous key + */ + @Override + public K previousKey(final Object key) { + final LinkEntry entry = getEntry(key); + return entry == null || entry.before == header ? null : entry.before.getKey(); + } + + //----------------------------------------------------------------------- + /** + * Gets the key at the specified index. + * + * @param index the index to retrieve + * @return the key at the specified index + * @throws IndexOutOfBoundsException if the index is invalid + */ + protected LinkEntry getEntry(final int index) { + if (index < 0) { + throw new IndexOutOfBoundsException("Index " + index + " is less than zero"); + } + if (index >= size) { + throw new IndexOutOfBoundsException("Index " + index + " is invalid for size " + size); + } + LinkEntry entry; + if (index < size / 2) { + // Search forwards + entry = header.after; + for (int currentIndex = 0; currentIndex < index; currentIndex++) { + entry = entry.after; + } + } else { + // Search backwards + entry = header; + for (int currentIndex = size; currentIndex > index; currentIndex--) { + entry = entry.before; + } + } + return entry; + } + + /** + * Adds an entry into this map, maintaining insertion order. + *

+ * This implementation adds the entry to the data storage table and + * to the end of the linked list. + * + * @param entry the entry to add + * @param hashIndex the index into the data array to store at + */ + @Override + protected void addEntry(final HashEntry entry, final int hashIndex) { + final LinkEntry link = (LinkEntry) entry; + link.after = header; + link.before = header.before; + header.before.after = link; + header.before = link; + data[hashIndex] = link; + } + + /** + * Creates an entry to store the data. + *

+ * This implementation creates a new LinkEntry instance. + * + * @param next the next entry in sequence + * @param hashCode the hash code to use + * @param key the key to store + * @param value the value to store + * @return the newly created entry + */ + @Override + protected LinkEntry createEntry(final HashEntry next, final int hashCode, final K key, final V value) { + return new LinkEntry<>(next, hashCode, convertKey(key), value); + } + + /** + * Removes an entry from the map and the linked list. + *

+ * This implementation removes the entry from the linked list chain, then + * calls the superclass implementation. + * + * @param entry the entry to remove + * @param hashIndex the index into the data structure + * @param previous the previous entry in the chain + */ + @Override + protected void removeEntry(final HashEntry entry, final int hashIndex, final HashEntry previous) { + final LinkEntry link = (LinkEntry) entry; + link.before.after = link.after; + link.after.before = link.before; + link.after = null; + link.before = null; + super.removeEntry(entry, hashIndex, previous); + } + + //----------------------------------------------------------------------- + /** + * Gets the before field from a LinkEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the before field of the entry + * @throws NullPointerException if the entry is null + * @since 3.1 + */ + protected LinkEntry entryBefore(final LinkEntry entry) { + return entry.before; + } + + /** + * Gets the after field from a LinkEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the after field of the entry + * @throws NullPointerException if the entry is null + * @since 3.1 + */ + protected LinkEntry entryAfter(final LinkEntry entry) { + return entry.after; + } + + //----------------------------------------------------------------------- + /** + * {@inheritDoc} + */ + @Override + public OrderedMapIterator mapIterator() { + if (size == 0) { + return EmptyOrderedMapIterator.emptyOrderedMapIterator(); + } + return new LinkMapIterator<>(this); + } + + /** + * MapIterator implementation. + */ + protected static class LinkMapIterator extends LinkIterator implements + OrderedMapIterator, ResettableIterator { + + protected LinkMapIterator(final AbstractLinkedMap parent) { + super(parent); + } + + @Override + public K next() { + return super.nextEntry().getKey(); + } + + @Override + public K previous() { + return super.previousEntry().getKey(); + } + + @Override + public K getKey() { + final LinkEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); + } + return current.getKey(); + } + + @Override + public V getValue() { + final LinkEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(GETVALUE_INVALID); + } + return current.getValue(); + } + + @Override + public V setValue(final V value) { + final LinkEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(SETVALUE_INVALID); + } + return current.setValue(value); + } + } + + //----------------------------------------------------------------------- + /** + * Creates an entry set iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the entrySet iterator + */ + @Override + protected Iterator> createEntrySetIterator() { + if (size() == 0) { + return EmptyOrderedIterator.>emptyOrderedIterator(); + } + return new EntrySetIterator<>(this); + } + + /** + * EntrySet iterator. + */ + protected static class EntrySetIterator extends LinkIterator implements + OrderedIterator>, ResettableIterator> { + + protected EntrySetIterator(final AbstractLinkedMap parent) { + super(parent); + } + + @Override + public Entry next() { + return super.nextEntry(); + } + + @Override + public Entry previous() { + return super.previousEntry(); + } + } + + //----------------------------------------------------------------------- + /** + * Creates a key set iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the keySet iterator + */ + @Override + protected Iterator createKeySetIterator() { + if (size() == 0) { + return EmptyOrderedIterator.emptyOrderedIterator(); + } + return new KeySetIterator<>(this); + } + + /** + * KeySet iterator. + */ + protected static class KeySetIterator extends LinkIterator implements + OrderedIterator, ResettableIterator { + + @SuppressWarnings("unchecked") + protected KeySetIterator(final AbstractLinkedMap parent) { + super((AbstractLinkedMap) parent); + } + + @Override + public K next() { + return super.nextEntry().getKey(); + } + + @Override + public K previous() { + return super.previousEntry().getKey(); + } + } + + //----------------------------------------------------------------------- + /** + * Creates a values iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the values iterator + */ + @Override + protected Iterator createValuesIterator() { + if (size() == 0) { + return EmptyOrderedIterator.emptyOrderedIterator(); + } + return new ValuesIterator<>(this); + } + + /** + * Values iterator. + */ + protected static class ValuesIterator extends LinkIterator implements + OrderedIterator, ResettableIterator { + + @SuppressWarnings("unchecked") + protected ValuesIterator(final AbstractLinkedMap parent) { + super((AbstractLinkedMap) parent); + } + + @Override + public V next() { + return super.nextEntry().getValue(); + } + + @Override + public V previous() { + return super.previousEntry().getValue(); + } + } + + //----------------------------------------------------------------------- + /** + * LinkEntry that stores the data. + *

+ * If you subclass AbstractLinkedMap but not LinkEntry + * then you will not be able to access the protected fields. + * The entryXxx() methods on AbstractLinkedMap exist + * to provide the necessary access. + */ + protected static class LinkEntry extends HashEntry { + /** The entry before this one in the order */ + protected LinkEntry before; + /** The entry after this one in the order */ + protected LinkEntry after; + + /** + * Constructs a new entry. + * + * @param next the next entry in the hash bucket sequence + * @param hashCode the hash code + * @param key the key + * @param value the value + */ + protected LinkEntry(final HashEntry next, final int hashCode, final Object key, final V value) { + super(next, hashCode, key, value); + } + } + + /** + * Base Iterator that iterates in link order. + */ + protected static abstract class LinkIterator { + + /** The parent map */ + protected final AbstractLinkedMap parent; + /** The current (last returned) entry */ + protected LinkEntry last; + /** The next entry */ + protected LinkEntry next; + /** The modification count expected */ + protected int expectedModCount; + + protected LinkIterator(final AbstractLinkedMap parent) { + super(); + this.parent = parent; + this.next = parent.header.after; + this.expectedModCount = parent.modCount; + } + + public boolean hasNext() { + return next != parent.header; + } + + public boolean hasPrevious() { + return next.before != parent.header; + } + + protected LinkEntry nextEntry() { + if (parent.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + if (next == parent.header) { + throw new NoSuchElementException(NO_NEXT_ENTRY); + } + last = next; + next = next.after; + return last; + } + + protected LinkEntry previousEntry() { + if (parent.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + final LinkEntry previous = next.before; + if (previous == parent.header) { + throw new NoSuchElementException(AbstractHashedMap.NO_PREVIOUS_ENTRY); + } + next = previous; + last = previous; + return last; + } + + protected LinkEntry currentEntry() { + return last; + } + + public void remove() { + if (last == null) { + throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID); + } + if (parent.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + parent.remove(last.getKey()); + last = null; + expectedModCount = parent.modCount; + } + + public void reset() { + last = null; + next = parent.header.after; + } + + @Override + public String toString() { + if (last != null) { + return "Iterator[" + last.getKey() + "=" + last.getValue() + "]"; + } + return "Iterator[]"; + } + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractListDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractListDecorator.java index c80676ba4f..133d5ba539 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractListDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractListDecorator.java @@ -1,128 +1,128 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Collection; -import java.util.List; -import java.util.ListIterator; - -/** - * Decorates another {@link List} to provide additional behaviour. - *

- * Methods are forwarded directly to the decorated list. - *

- * - * @param the type of the elements in the list - * @since 3.0 - */ -public abstract class AbstractListDecorator extends AbstractCollectionDecorator - implements List { - - /** Serialization version--necessary in an abstract class? */ - private static final long serialVersionUID = 4500739654952315623L; - - /** - * Constructor only used in deserialization, do not use otherwise. - * @since 3.1 - */ - protected AbstractListDecorator() { - super(); - } - - /** - * Constructor that wraps (not copies). - * - * @param list the list to decorate, must not be null - * @throws NullPointerException if list is null - */ - protected AbstractListDecorator(final List list) { - super(list); - } - - /** - * Gets the list being decorated. - * - * @return the decorated list - */ - @Override - protected List decorated() { - return (List) super.decorated(); - } - - @Override - public boolean equals(final Object object) { - return object == this || decorated().equals(object); - } - - @Override - public int hashCode() { - return decorated().hashCode(); - } - - //----------------------------------------------------------------------- - - @Override - public void add(final int index, final E object) { - decorated().add(index, object); - } - - @Override - public boolean addAll(final int index, final Collection coll) { - return decorated().addAll(index, coll); - } - - @Override - public E get(final int index) { - return decorated().get(index); - } - - @Override - public int indexOf(final Object object) { - return decorated().indexOf(object); - } - - @Override - public int lastIndexOf(final Object object) { - return decorated().lastIndexOf(object); - } - - @Override - public ListIterator listIterator() { - return decorated().listIterator(); - } - - @Override - public ListIterator listIterator(final int index) { - return decorated().listIterator(index); - } - - @Override - public E remove(final int index) { - return decorated().remove(index); - } - - @Override - public E set(final int index, final E object) { - return decorated().set(index, object); - } - - @Override - public List subList(final int fromIndex, final int toIndex) { - return decorated().subList(fromIndex, toIndex); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; + +/** + * Decorates another {@link List} to provide additional behaviour. + *

+ * Methods are forwarded directly to the decorated list. + *

+ * + * @param the type of the elements in the list + * @since 3.0 + */ +public abstract class AbstractListDecorator extends AbstractCollectionDecorator + implements List { + + /** Serialization version--necessary in an abstract class? */ + private static final long serialVersionUID = 4500739654952315623L; + + /** + * Constructor only used in deserialization, do not use otherwise. + * @since 3.1 + */ + protected AbstractListDecorator() { + super(); + } + + /** + * Constructor that wraps (not copies). + * + * @param list the list to decorate, must not be null + * @throws NullPointerException if list is null + */ + protected AbstractListDecorator(final List list) { + super(list); + } + + /** + * Gets the list being decorated. + * + * @return the decorated list + */ + @Override + protected List decorated() { + return (List) super.decorated(); + } + + @Override + public boolean equals(final Object object) { + return object == this || decorated().equals(object); + } + + @Override + public int hashCode() { + return decorated().hashCode(); + } + + //----------------------------------------------------------------------- + + @Override + public void add(final int index, final E object) { + decorated().add(index, object); + } + + @Override + public boolean addAll(final int index, final Collection coll) { + return decorated().addAll(index, coll); + } + + @Override + public E get(final int index) { + return decorated().get(index); + } + + @Override + public int indexOf(final Object object) { + return decorated().indexOf(object); + } + + @Override + public int lastIndexOf(final Object object) { + return decorated().lastIndexOf(object); + } + + @Override + public ListIterator listIterator() { + return decorated().listIterator(); + } + + @Override + public ListIterator listIterator(final int index) { + return decorated().listIterator(index); + } + + @Override + public E remove(final int index) { + return decorated().remove(index); + } + + @Override + public E set(final int index, final E object) { + return decorated().set(index, object); + } + + @Override + public List subList(final int fromIndex, final int toIndex) { + return decorated().subList(fromIndex, toIndex); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapDecorator.java index 93eb0e567f..c1db003647 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapDecorator.java @@ -1,156 +1,156 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -/** - * Provides a base decorator that enables additional functionality to be added - * to a Map via decoration. - *

- * Methods are forwarded directly to the decorated map. - *

- *

- * This implementation does not perform any special processing with - * {@link #entrySet()}, {@link #keySet()} or {@link #values()}. Instead - * it simply returns the set/collection from the wrapped map. This may be - * undesirable, for example if you are trying to write a validating - * implementation it would provide a loophole around the validation. - * But, you might want that loophole, so this class is kept simple. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * @since 3.0 - */ -public abstract class AbstractMapDecorator extends AbstractIterableMap { - - /** The map to decorate */ - transient Map map; - - /** - * Constructor only used in deserialization, do not use otherwise. - * @since 3.1 - */ - protected AbstractMapDecorator() { - super(); - } - - /** - * Constructor that wraps (not copies). - * - * @param map the map to decorate, must not be null - * @throws NullPointerException if the map is null - */ - protected AbstractMapDecorator(final Map map) { - if (map == null) { - throw new NullPointerException("Map must not be null."); - } - this.map = map; - } - - /** - * Gets the map being decorated. - * - * @return the decorated map - */ - protected Map decorated() { - return map; - } - - //----------------------------------------------------------------------- - @Override - public void clear() { - decorated().clear(); - } - - @Override - public boolean containsKey(final Object key) { - return decorated().containsKey(key); - } - - @Override - public boolean containsValue(final Object value) { - return decorated().containsValue(value); - } - - @Override - public Set> entrySet() { - return decorated().entrySet(); - } - - @Override - public V get(final Object key) { - return decorated().get(key); - } - - @Override - public boolean isEmpty() { - return decorated().isEmpty(); - } - - @Override - public Set keySet() { - return decorated().keySet(); - } - - @Override - public V put(final K key, final V value) { - return decorated().put(key, value); - } - - @Override - public void putAll(final Map mapToCopy) { - decorated().putAll(mapToCopy); - } - - @Override - public V remove(final Object key) { - return decorated().remove(key); - } - - @Override - public int size() { - return decorated().size(); - } - - @Override - public Collection values() { - return decorated().values(); - } - - @Override - public boolean equals(final Object object) { - if (object == this) { - return true; - } - return decorated().equals(object); - } - - @Override - public int hashCode() { - return decorated().hashCode(); - } - - @Override - public String toString() { - return decorated().toString(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Provides a base decorator that enables additional functionality to be added + * to a Map via decoration. + *

+ * Methods are forwarded directly to the decorated map. + *

+ *

+ * This implementation does not perform any special processing with + * {@link #entrySet()}, {@link #keySet()} or {@link #values()}. Instead + * it simply returns the set/collection from the wrapped map. This may be + * undesirable, for example if you are trying to write a validating + * implementation it would provide a loophole around the validation. + * But, you might want that loophole, so this class is kept simple. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * @since 3.0 + */ +public abstract class AbstractMapDecorator extends AbstractIterableMap { + + /** The map to decorate */ + transient Map map; + + /** + * Constructor only used in deserialization, do not use otherwise. + * @since 3.1 + */ + protected AbstractMapDecorator() { + super(); + } + + /** + * Constructor that wraps (not copies). + * + * @param map the map to decorate, must not be null + * @throws NullPointerException if the map is null + */ + protected AbstractMapDecorator(final Map map) { + if (map == null) { + throw new NullPointerException("Map must not be null."); + } + this.map = map; + } + + /** + * Gets the map being decorated. + * + * @return the decorated map + */ + protected Map decorated() { + return map; + } + + //----------------------------------------------------------------------- + @Override + public void clear() { + decorated().clear(); + } + + @Override + public boolean containsKey(final Object key) { + return decorated().containsKey(key); + } + + @Override + public boolean containsValue(final Object value) { + return decorated().containsValue(value); + } + + @Override + public Set> entrySet() { + return decorated().entrySet(); + } + + @Override + public V get(final Object key) { + return decorated().get(key); + } + + @Override + public boolean isEmpty() { + return decorated().isEmpty(); + } + + @Override + public Set keySet() { + return decorated().keySet(); + } + + @Override + public V put(final K key, final V value) { + return decorated().put(key, value); + } + + @Override + public void putAll(final Map mapToCopy) { + decorated().putAll(mapToCopy); + } + + @Override + public V remove(final Object key) { + return decorated().remove(key); + } + + @Override + public int size() { + return decorated().size(); + } + + @Override + public Collection values() { + return decorated().values(); + } + + @Override + public boolean equals(final Object object) { + if (object == this) { + return true; + } + return decorated().equals(object); + } + + @Override + public int hashCode() { + return decorated().hashCode(); + } + + @Override + public String toString() { + return decorated().toString(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapEntry.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapEntry.java index 530016ebef..f331a8d262 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapEntry.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapEntry.java @@ -1,92 +1,92 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Map; - -/** - * Abstract Pair class to assist with creating correct - * {@link Map.Entry Map.Entry} implementations. - * - * @param the type of keys - * @param the type of mapped values - * @since 3.0 - */ -public abstract class AbstractMapEntry extends AbstractKeyValue implements Map.Entry { - - /** - * Constructs a new entry with the given key and given value. - * - * @param key the key for the entry, may be null - * @param value the value for the entry, may be null - */ - protected AbstractMapEntry(final K key, final V value) { - super(key, value); - } - - // Map.Entry interface - //------------------------------------------------------------------------- - /** - * Sets the value stored in this Map.Entry. - *

- * This Map.Entry is not connected to a Map, so only the - * local data is changed. - * - * @param value the new value - * @return the previous value - */ - @Override - public V setValue(final V value) { // NOPMD - return super.setValue(value); - } - - /** - * Compares this Map.Entry with another Map.Entry. - *

- * Implemented per API documentation of {@link Map.Entry#equals(Object)} - * - * @param obj the object to compare to - * @return true if equal key and value - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Map.Entry)) { - return false; - } - final Map.Entry other = (Map.Entry) obj; - return - (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) && - (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())); - } - - /** - * Gets a hashCode compatible with the equals method. - *

- * Implemented per API documentation of {@link Map.Entry#hashCode()} - * - * @return a suitable hash code - */ - @Override - public int hashCode() { - return (getKey() == null ? 0 : getKey().hashCode()) ^ - (getValue() == null ? 0 : getValue().hashCode()); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Map; + +/** + * Abstract Pair class to assist with creating correct + * {@link Map.Entry Map.Entry} implementations. + * + * @param the type of keys + * @param the type of mapped values + * @since 3.0 + */ +public abstract class AbstractMapEntry extends AbstractKeyValue implements Map.Entry { + + /** + * Constructs a new entry with the given key and given value. + * + * @param key the key for the entry, may be null + * @param value the value for the entry, may be null + */ + protected AbstractMapEntry(final K key, final V value) { + super(key, value); + } + + // Map.Entry interface + //------------------------------------------------------------------------- + /** + * Sets the value stored in this Map.Entry. + *

+ * This Map.Entry is not connected to a Map, so only the + * local data is changed. + * + * @param value the new value + * @return the previous value + */ + @Override + public V setValue(final V value) { // NOPMD + return super.setValue(value); + } + + /** + * Compares this Map.Entry with another Map.Entry. + *

+ * Implemented per API documentation of {@link Map.Entry#equals(Object)} + * + * @param obj the object to compare to + * @return true if equal key and value + */ + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Map.Entry)) { + return false; + } + final Map.Entry other = (Map.Entry) obj; + return + (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) && + (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())); + } + + /** + * Gets a hashCode compatible with the equals method. + *

+ * Implemented per API documentation of {@link Map.Entry#hashCode()} + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return (getKey() == null ? 0 : getKey().hashCode()) ^ + (getValue() == null ? 0 : getValue().hashCode()); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapEntryDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapEntryDecorator.java index 1fbb1ea52e..c5ec300a97 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapEntryDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractMapEntryDecorator.java @@ -1,91 +1,91 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Map; - -/** - * Provides a base decorator that allows additional functionality to be - * added to a {@link Map.Entry Map.Entry}. - * - * @param the type of keys - * @param the type of mapped values - * @since 3.0 - */ -public abstract class AbstractMapEntryDecorator implements Map.Entry, KeyValue { - - /** The Map.Entry to decorate */ - private final Map.Entry entry; - - /** - * Constructor that wraps (not copies). - * - * @param entry the Map.Entry to decorate, must not be null - * @throws NullPointerException if the collection is null - */ - public AbstractMapEntryDecorator(final Map.Entry entry) { - if (entry == null) { - throw new NullPointerException("Map Entry must not be null."); - } - this.entry = entry; - } - - /** - * Gets the map being decorated. - * - * @return the decorated map - */ - protected Map.Entry getMapEntry() { - return entry; - } - - //----------------------------------------------------------------------- - - @Override - public K getKey() { - return entry.getKey(); - } - - @Override - public V getValue() { - return entry.getValue(); - } - - @Override - public V setValue(final V object) { - return entry.setValue(object); - } - - @Override - public boolean equals(final Object object) { - if (object == this) { - return true; - } - return entry.equals(object); - } - - @Override - public int hashCode() { - return entry.hashCode(); - } - - @Override - public String toString() { - return entry.toString(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Map; + +/** + * Provides a base decorator that allows additional functionality to be + * added to a {@link Map.Entry Map.Entry}. + * + * @param the type of keys + * @param the type of mapped values + * @since 3.0 + */ +public abstract class AbstractMapEntryDecorator implements Map.Entry, KeyValue { + + /** The Map.Entry to decorate */ + private final Map.Entry entry; + + /** + * Constructor that wraps (not copies). + * + * @param entry the Map.Entry to decorate, must not be null + * @throws NullPointerException if the collection is null + */ + public AbstractMapEntryDecorator(final Map.Entry entry) { + if (entry == null) { + throw new NullPointerException("Map Entry must not be null."); + } + this.entry = entry; + } + + /** + * Gets the map being decorated. + * + * @return the decorated map + */ + protected Map.Entry getMapEntry() { + return entry; + } + + //----------------------------------------------------------------------- + + @Override + public K getKey() { + return entry.getKey(); + } + + @Override + public V getValue() { + return entry.getValue(); + } + + @Override + public V setValue(final V object) { + return entry.setValue(object); + } + + @Override + public boolean equals(final Object object) { + if (object == this) { + return true; + } + return entry.equals(object); + } + + @Override + public int hashCode() { + return entry.hashCode(); + } + + @Override + public String toString() { + return entry.toString(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractOrderedMapDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractOrderedMapDecorator.java index 61cd5cc489..18643beab5 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractOrderedMapDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractOrderedMapDecorator.java @@ -1,94 +1,94 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Provides a base decorator that enables additional functionality to be added - * to an OrderedMap via decoration. - *

- * Methods are forwarded directly to the decorated map. - *

- *

- * This implementation does not perform any special processing with the map views. - * Instead it simply returns the set/collection from the wrapped map. This may be - * undesirable, for example if you are trying to write a validating implementation - * it would provide a loophole around the validation. - * But, you might want that loophole, so this class is kept simple. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * @since 3.0 - */ -public abstract class AbstractOrderedMapDecorator extends AbstractMapDecorator - implements OrderedMap { - - /** - * Constructor only used in deserialization, do not use otherwise. - * @since 3.1 - */ - protected AbstractOrderedMapDecorator() { - super(); - } - - /** - * Constructor that wraps (not copies). - * - * @param map the map to decorate, must not be null - * @throws NullPointerException if the map is null - */ - public AbstractOrderedMapDecorator(final OrderedMap map) { - super(map); - } - - /** - * Gets the map being decorated. - * - * @return the decorated map - */ - @Override - protected OrderedMap decorated() { - return (OrderedMap) super.decorated(); - } - - //----------------------------------------------------------------------- - @Override - public K firstKey() { - return decorated().firstKey(); - } - - @Override - public K lastKey() { - return decorated().lastKey(); - } - - @Override - public K nextKey(final K key) { - return decorated().nextKey(key); - } - - @Override - public K previousKey(final K key) { - return decorated().previousKey(key); - } - - @Override - public OrderedMapIterator mapIterator() { - return decorated().mapIterator(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Provides a base decorator that enables additional functionality to be added + * to an OrderedMap via decoration. + *

+ * Methods are forwarded directly to the decorated map. + *

+ *

+ * This implementation does not perform any special processing with the map views. + * Instead it simply returns the set/collection from the wrapped map. This may be + * undesirable, for example if you are trying to write a validating implementation + * it would provide a loophole around the validation. + * But, you might want that loophole, so this class is kept simple. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * @since 3.0 + */ +public abstract class AbstractOrderedMapDecorator extends AbstractMapDecorator + implements OrderedMap { + + /** + * Constructor only used in deserialization, do not use otherwise. + * @since 3.1 + */ + protected AbstractOrderedMapDecorator() { + super(); + } + + /** + * Constructor that wraps (not copies). + * + * @param map the map to decorate, must not be null + * @throws NullPointerException if the map is null + */ + public AbstractOrderedMapDecorator(final OrderedMap map) { + super(map); + } + + /** + * Gets the map being decorated. + * + * @return the decorated map + */ + @Override + protected OrderedMap decorated() { + return (OrderedMap) super.decorated(); + } + + //----------------------------------------------------------------------- + @Override + public K firstKey() { + return decorated().firstKey(); + } + + @Override + public K lastKey() { + return decorated().lastKey(); + } + + @Override + public K nextKey(final K key) { + return decorated().nextKey(key); + } + + @Override + public K previousKey(final K key) { + return decorated().previousKey(key); + } + + @Override + public OrderedMapIterator mapIterator() { + return decorated().mapIterator(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractReferenceMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractReferenceMap.java index da46be875e..06caf8adae 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractReferenceMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractReferenceMap.java @@ -1,1107 +1,1107 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -/** - * An abstract implementation of a hash-based map that allows the entries to - * be removed by the garbage collector. - *

- * This class implements all the features necessary for a subclass reference - * hash-based map. Key-value entries are stored in instances of the - * ReferenceEntry class which can be overridden and replaced. - * The iterators can similarly be replaced, without the need to replace the KeySet, - * EntrySet and Values view classes. - *

- *

- * Overridable methods are provided to change the default hashing behaviour, and - * to change how entries are added to and removed from the map. Hopefully, all you - * need for unusual subclasses is here. - *

- *

- * When you construct an AbstractReferenceMap, you can specify what - * kind of references are used to store the map's keys and values. - * If non-hard references are used, then the garbage collector can remove - * mappings if a key or value becomes unreachable, or if the JVM's memory is - * running low. For information on how the different reference types behave, - * see {@link Reference}. - *

- *

- * Different types of references can be specified for keys and values. - * The keys can be configured to be weak but the values hard, - * in which case this class will behave like a - * - * WeakHashMap. However, you can also specify hard keys and - * weak values, or any other combination. The default constructor uses - * hard keys and soft values, providing a memory-sensitive cache. - *

- *

- * This {@link Map} implementation does not allow null elements. - * Attempting to add a null key or value to the map will raise a - * NullPointerException. - *

- *

- * All the available iterators can be reset back to the start by casting to - * ResettableIterator and calling reset(). - *

- *

- * This implementation is not synchronized. - * You can use {@link java.util.Collections#synchronizedMap} to - * provide synchronized access to a ReferenceMap. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * - * @see Reference - * @since 3.1 (extracted from ReferenceMap in 3.0) - */ -public abstract class AbstractReferenceMap extends AbstractHashedMap { - - /** - * Reference type enum. - */ - public enum ReferenceStrength { - HARD(0), SOFT(1), WEAK(2); - - /** value */ - public final int value; - - /** - * Resolve enum from int. - * @param value the int value - * @return ReferenceType - * @throws IllegalArgumentException if the specified value is invalid. - */ - public static ReferenceStrength resolve(final int value) { - switch (value) { - case 0: - return HARD; - case 1: - return SOFT; - case 2: - return WEAK; - default: - throw new IllegalArgumentException(); - } - } - - ReferenceStrength(final int value) { - this.value = value; - } - - } - - /** - * The reference type for keys. - */ - private ReferenceStrength keyType; - - /** - * The reference type for values. - */ - private ReferenceStrength valueType; - - /** - * Should the value be automatically purged when the associated key has been collected? - */ - private boolean purgeValues; - - /** - * ReferenceQueue used to eliminate stale mappings. - * See purge. - */ - private transient ReferenceQueue queue; - - //----------------------------------------------------------------------- - /** - * Constructor used during deserialization. - */ - protected AbstractReferenceMap() { - super(); - } - - /** - * Constructs a new empty map with the specified reference types, - * load factor and initial capacity. - * - * @param keyType the type of reference to use for keys; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param valueType the type of reference to use for values; - * must be {@link ReferenceStrength#HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param capacity the initial capacity for the map - * @param loadFactor the load factor for the map - * @param purgeValues should the value be automatically purged when the - * key is garbage collected - */ - protected AbstractReferenceMap( - final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity, - final float loadFactor, final boolean purgeValues) { - super(capacity, loadFactor); - this.keyType = keyType; - this.valueType = valueType; - this.purgeValues = purgeValues; - } - - /** - * Initialise this subclass during construction, cloning or deserialization. - */ - @Override - protected void init() { - queue = new ReferenceQueue<>(); - } - - //----------------------------------------------------------------------- - /** - * Gets the size of the map. - * - * @return the size - */ - @Override - public int size() { - purgeBeforeRead(); - return super.size(); - } - - /** - * Checks whether the map is currently empty. - * - * @return true if the map is currently size zero - */ - @Override - public boolean isEmpty() { - purgeBeforeRead(); - return super.isEmpty(); - } - - /** - * Checks whether the map contains the specified key. - * - * @param key the key to search for - * @return true if the map contains the key - */ - @Override - public boolean containsKey(final Object key) { - purgeBeforeRead(); - final Entry entry = getEntry(key); - if (entry == null) { - return false; - } - return entry.getValue() != null; - } - - /** - * Checks whether the map contains the specified value. - * - * @param value the value to search for - * @return true if the map contains the value - */ - @Override - public boolean containsValue(final Object value) { - purgeBeforeRead(); - if (value == null) { - return false; - } - return super.containsValue(value); - } - - /** - * Gets the value mapped to the key specified. - * - * @param key the key - * @return the mapped value, null if no match - */ - @Override - public V get(final Object key) { - purgeBeforeRead(); - final Entry entry = getEntry(key); - if (entry == null) { - return null; - } - return entry.getValue(); - } - - - /** - * Puts a key-value mapping into this map. - * Neither the key nor the value may be null. - * - * @param key the key to add, must not be null - * @param value the value to add, must not be null - * @return the value previously mapped to this key, null if none - * @throws NullPointerException if either the key or value is null - */ - @Override - public V put(final K key, final V value) { - if (key == null) { - throw new NullPointerException("null keys not allowed"); - } - if (value == null) { - throw new NullPointerException("null values not allowed"); - } - - purgeBeforeWrite(); - return super.put(key, value); - } - - /** - * Removes the specified mapping from this map. - * - * @param key the mapping to remove - * @return the value mapped to the removed key, null if key not in map - */ - @Override - public V remove(final Object key) { - if (key == null) { - return null; - } - purgeBeforeWrite(); - return super.remove(key); - } - - /** - * Clears this map. - */ - @Override - public void clear() { - super.clear(); - // drain the queue - while (queue.poll() != null) { - // empty - } - } - - //----------------------------------------------------------------------- - /** - * Gets a MapIterator over the reference map. - * The iterator only returns valid key/value pairs. - * - * @return a map iterator - */ - @Override - public MapIterator mapIterator() { - return new ReferenceMapIterator<>(this); - } - - /** - * Returns a set view of this map's entries. - * An iterator returned entry is valid until next() is called again. - * The setValue() method on the toArray entries has no effect. - * - * @return a set view of this map's entries - */ - @Override - public Set> entrySet() { - if (entrySet == null) { - entrySet = new ReferenceEntrySet<>(this); - } - return entrySet; - } - - /** - * Returns a set view of this map's keys. - * - * @return a set view of this map's keys - */ - @Override - public Set keySet() { - if (keySet == null) { - keySet = new ReferenceKeySet<>(this); - } - return keySet; - } - - /** - * Returns a collection view of this map's values. - * - * @return a set view of this map's values - */ - @Override - public Collection values() { - if (values == null) { - values = new ReferenceValues<>(this); - } - return values; - } - - //----------------------------------------------------------------------- - /** - * Purges stale mappings from this map before read operations. - *

- * This implementation calls {@link #purge()} to maintain a consistent state. - */ - protected void purgeBeforeRead() { - purge(); - } - - /** - * Purges stale mappings from this map before write operations. - *

- * This implementation calls {@link #purge()} to maintain a consistent state. - */ - protected void purgeBeforeWrite() { - purge(); - } - - /** - * Purges stale mappings from this map. - *

- * Note that this method is not synchronized! Special - * care must be taken if, for instance, you want stale - * mappings to be removed on a periodic basis by some - * background thread. - */ - protected void purge() { - Reference ref = queue.poll(); - while (ref != null) { - purge(ref); - ref = queue.poll(); - } - } - - /** - * Purges the specified reference. - * - * @param ref the reference to purge - */ - protected void purge(final Reference ref) { - // The hashCode of the reference is the hashCode of the - // mapping key, even if the reference refers to the - // mapping value... - final int hash = ref.hashCode(); - final int index = hashIndex(hash, data.length); - HashEntry previous = null; - HashEntry entry = data[index]; - while (entry != null) { - ReferenceEntry refEntry = (ReferenceEntry) entry; - if (refEntry.purge(ref)) { - if (previous == null) { - data[index] = entry.next; - } else { - previous.next = entry.next; - } - this.size--; - refEntry.onPurge(); - return; - } - previous = entry; - entry = entry.next; - } - - } - - //----------------------------------------------------------------------- - /** - * Gets the entry mapped to the key specified. - * - * @param key the key - * @return the entry, null if no match - */ - @Override - protected HashEntry getEntry(final Object key) { - if (key == null) { - return null; - } - return super.getEntry(key); - } - - /** - * Gets the hash code for a MapEntry. - * Subclasses can override this, for example to use the identityHashCode. - * - * @param key the key to get a hash code for, may be null - * @param value the value to get a hash code for, may be null - * @return the hash code, as per the MapEntry specification - */ - protected int hashEntry(final Object key, final Object value) { - return (key == null ? 0 : key.hashCode()) ^ - (value == null ? 0 : value.hashCode()); - } - - /** - * Compares two keys, in internal converted form, to see if they are equal. - *

- * This implementation converts the key from the entry to a real reference - * before comparison. - * - * @param key1 the first key to compare passed in from outside - * @param key2 the second key extracted from the entry via entry.key - * @return true if equal - */ - @Override - @SuppressWarnings("unchecked") - protected boolean isEqualKey(final Object key1, Object key2) { - key2 = keyType == ReferenceStrength.HARD ? key2 : ((Reference) key2).get(); - return key1 == key2 || key1.equals(key2); - } - - /** - * Creates a ReferenceEntry instead of a HashEntry. - * - * @param next the next entry in sequence - * @param hashCode the hash code to use - * @param key the key to store - * @param value the value to store - * @return the newly created entry - */ - @Override - protected ReferenceEntry createEntry(final HashEntry next, final int hashCode, - final K key, final V value) { - return new ReferenceEntry<>(this, next, hashCode, key, value); - } - - /** - * Creates an entry set iterator. - * - * @return the entrySet iterator - */ - @Override - protected Iterator> createEntrySetIterator() { - return new ReferenceEntrySetIterator<>(this); - } - - /** - * Creates an key set iterator. - * - * @return the keySet iterator - */ - @Override - protected Iterator createKeySetIterator() { - return new ReferenceKeySetIterator<>(this); - } - - /** - * Creates an values iterator. - * - * @return the values iterator - */ - @Override - protected Iterator createValuesIterator() { - return new ReferenceValuesIterator<>(this); - } - - //----------------------------------------------------------------------- - /** - * EntrySet implementation. - */ - static class ReferenceEntrySet extends EntrySet { - - protected ReferenceEntrySet(final AbstractHashedMap parent) { - super(parent); - } - - @Override - public Object[] toArray() { - return toArray(new Object[size()]); - } - - @Override - public T[] toArray(final T[] arr) { - // special implementation to handle disappearing entries - final ArrayList> list = new ArrayList<>(size()); - for (final Entry entry : this) { - list.add(new DefaultMapEntry<>(entry)); - } - return list.toArray(arr); - } - } - - //----------------------------------------------------------------------- - /** - * KeySet implementation. - */ - static class ReferenceKeySet extends KeySet { - - protected ReferenceKeySet(final AbstractHashedMap parent) { - super(parent); - } - - @Override - public Object[] toArray() { - return toArray(new Object[size()]); - } - - @Override - public T[] toArray(final T[] arr) { - // special implementation to handle disappearing keys - final List list = new ArrayList<>(size()); - for (final K key : this) { - list.add(key); - } - return list.toArray(arr); - } - } - - //----------------------------------------------------------------------- - /** - * Values implementation. - */ - static class ReferenceValues extends Values { - - protected ReferenceValues(final AbstractHashedMap parent) { - super(parent); - } - - @Override - public Object[] toArray() { - return toArray(new Object[size()]); - } - - @Override - public T[] toArray(final T[] arr) { - // special implementation to handle disappearing values - final List list = new ArrayList<>(size()); - for (final V value : this) { - list.add(value); - } - return list.toArray(arr); - } - } - - //----------------------------------------------------------------------- - /** - * A MapEntry implementation for the map. - *

- * If getKey() or getValue() returns null, it means - * the mapping is stale and should be removed. - * - * @since 3.1 - */ - protected static class ReferenceEntry extends HashEntry { - /** The parent map */ - private final AbstractReferenceMap parent; - - /** - * Creates a new entry object for the ReferenceMap. - * - * @param parent the parent map - * @param next the next entry in the hash bucket - * @param hashCode the hash code of the key - * @param key the key - * @param value the value - */ - public ReferenceEntry(final AbstractReferenceMap parent, final HashEntry next, - final int hashCode, final K key, final V value) { - super(next, hashCode, null, null); - this.parent = parent; - this.key = toReference(parent.keyType, key, hashCode); - this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately - } - - /** - * Gets the key from the entry. - * This method dereferences weak and soft keys and thus may return null. - * - * @return the key, which may be null if it was garbage collected - */ - @Override - @SuppressWarnings("unchecked") - public K getKey() { - return (K) (parent.keyType == ReferenceStrength.HARD ? key : ((Reference) key).get()); - } - - /** - * Gets the value from the entry. - * This method dereferences weak and soft value and thus may return null. - * - * @return the value, which may be null if it was garbage collected - */ - @Override - @SuppressWarnings("unchecked") - public V getValue() { - return (V) (parent.valueType == ReferenceStrength.HARD ? value : ((Reference) value).get()); - } - - /** - * Sets the value of the entry. - * - * @param obj the object to store - * @return the previous value - */ - @Override - @SuppressWarnings("unchecked") - public V setValue(final V obj) { - final V old = getValue(); - if (parent.valueType != ReferenceStrength.HARD) { - ((Reference) value).clear(); - } - value = toReference(parent.valueType, obj, hashCode); - return old; - } - - /** - * Compares this map entry to another. - *

- * This implementation uses isEqualKey and - * isEqualValue on the main map for comparison. - * - * @param obj the other map entry to compare to - * @return true if equal, false if not - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Map.Entry)) { - return false; - } - - final Entry entry = (Entry)obj; - final Object entryKey = entry.getKey(); // convert to hard reference - final Object entryValue = entry.getValue(); // convert to hard reference - if (entryKey == null || entryValue == null) { - return false; - } - // compare using map methods, aiding identity subclass - // note that key is direct access and value is via method - return parent.isEqualKey(entryKey, key) && - parent.isEqualValue(entryValue, getValue()); - } - - /** - * Gets the hashcode of the entry using temporary hard references. - *

- * This implementation uses hashEntry on the main map. - * - * @return the hashcode of the entry - */ - @Override - public int hashCode() { - return parent.hashEntry(getKey(), getValue()); - } - - /** - * Constructs a reference of the given type to the given referent. - * The reference is registered with the queue for later purging. - * - * @param the type of the referenced object - * @param type HARD, SOFT or WEAK - * @param referent the object to refer to - * @param hash the hash code of the key of the mapping; - * this number might be different from referent.hashCode() if - * the referent represents a value and not a key - * @return the reference to the object - */ - protected Object toReference(final ReferenceStrength type, final T referent, final int hash) { - if (type == ReferenceStrength.HARD) { - return referent; - } - if (type == ReferenceStrength.SOFT) { - return new SoftRef<>(hash, referent, parent.queue); - } - if (type == ReferenceStrength.WEAK) { - return new WeakRef<>(hash, referent, parent.queue); - } - throw new Error(); - } - - /** - * This is the callback for custom "after purge" logic - */ - protected void onPurge() { - // empty - } - - /** - * Purges the specified reference - * @param ref the reference to purge - * @return true or false - */ - protected boolean purge(final Reference ref) { - boolean r = parent.keyType != ReferenceStrength.HARD && key == ref; - r = r || parent.valueType != ReferenceStrength.HARD && value == ref; - if (r) { - if (parent.keyType != ReferenceStrength.HARD) { - ((Reference) key).clear(); - } - if (parent.valueType != ReferenceStrength.HARD) { - ((Reference) value).clear(); - } else if (parent.purgeValues) { - nullValue(); - } - } - return r; - } - - /** - * Gets the next entry in the bucket. - * - * @return the next entry in the bucket - */ - protected ReferenceEntry next() { - return (ReferenceEntry) next; - } - - /** - * This method can be overriden to provide custom logic to purge value - */ - protected void nullValue() { - value = null; - } - } - - //----------------------------------------------------------------------- - /** - * Base iterator class. - */ - static class ReferenceBaseIterator { - /** The parent map */ - final AbstractReferenceMap parent; - - // These fields keep track of where we are in the table. - int index; - ReferenceEntry entry; - ReferenceEntry previous; - - // These Object fields provide hard references to the - // current and next entry; this assures that if hasNext() - // returns true, next() will actually return a valid element. - K currentKey, nextKey; - V currentValue, nextValue; - - int expectedModCount; - - public ReferenceBaseIterator(final AbstractReferenceMap parent) { - super(); - this.parent = parent; - index = parent.size() != 0 ? parent.data.length : 0; - // have to do this here! size() invocation above - // may have altered the modCount. - expectedModCount = parent.modCount; - } - - public boolean hasNext() { - checkMod(); - while (nextNull()) { - ReferenceEntry e = entry; - int i = index; - while (e == null && i > 0) { - i--; - e = (ReferenceEntry) parent.data[i]; - } - entry = e; - index = i; - if (e == null) { - currentKey = null; - currentValue = null; - return false; - } - nextKey = e.getKey(); - nextValue = e.getValue(); - if (nextNull()) { - entry = entry.next(); - } - } - return true; - } - - private void checkMod() { - if (parent.modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - } - - private boolean nextNull() { - return nextKey == null || nextValue == null; - } - - protected ReferenceEntry nextEntry() { - checkMod(); - if (nextNull() && !hasNext()) { - throw new NoSuchElementException(); - } - previous = entry; - entry = entry.next(); - currentKey = nextKey; - currentValue = nextValue; - nextKey = null; - nextValue = null; - return previous; - } - - protected ReferenceEntry currentEntry() { - checkMod(); - return previous; - } - - public void remove() { - checkMod(); - if (previous == null) { - throw new IllegalStateException(); - } - parent.remove(currentKey); - previous = null; - currentKey = null; - currentValue = null; - expectedModCount = parent.modCount; - } - } - - /** - * The EntrySet iterator. - */ - static class ReferenceEntrySetIterator - extends ReferenceBaseIterator implements Iterator> { - - public ReferenceEntrySetIterator(final AbstractReferenceMap parent) { - super(parent); - } - - @Override - public Entry next() { - return nextEntry(); - } - - } - - /** - * The keySet iterator. - */ - static class ReferenceKeySetIterator extends ReferenceBaseIterator implements Iterator { - - @SuppressWarnings("unchecked") - ReferenceKeySetIterator(final AbstractReferenceMap parent) { - super((AbstractReferenceMap) parent); - } - - @Override - public K next() { - return nextEntry().getKey(); - } - } - - /** - * The values iterator. - */ - static class ReferenceValuesIterator extends ReferenceBaseIterator implements Iterator { - - @SuppressWarnings("unchecked") - ReferenceValuesIterator(final AbstractReferenceMap parent) { - super((AbstractReferenceMap) parent); - } - - @Override - public V next() { - return nextEntry().getValue(); - } - } - - /** - * The MapIterator implementation. - */ - static class ReferenceMapIterator extends ReferenceBaseIterator implements MapIterator { - - protected ReferenceMapIterator(final AbstractReferenceMap parent) { - super(parent); - } - - @Override - public K next() { - return nextEntry().getKey(); - } - - @Override - public K getKey() { - final HashEntry current = currentEntry(); - if (current == null) { - throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); - } - return current.getKey(); - } - - @Override - public V getValue() { - final HashEntry current = currentEntry(); - if (current == null) { - throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); - } - return current.getValue(); - } - - @Override - public V setValue(final V value) { - final HashEntry current = currentEntry(); - if (current == null) { - throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); - } - return current.setValue(value); - } - } - - //----------------------------------------------------------------------- - // These two classes store the hashCode of the key of - // of the mapping, so that after they're dequeued a quick - // lookup of the bucket in the table can occur. - - /** - * A soft reference holder. - */ - static class SoftRef extends SoftReference { - /** the hashCode of the key (even if the reference points to a value) */ - private final int hash; - - public SoftRef(final int hash, final T r, final ReferenceQueue q) { - super(r, q); - this.hash = hash; - } - - @Override - public int hashCode() { - return hash; - } - } - - /** - * A weak reference holder. - */ - static class WeakRef extends WeakReference { - /** the hashCode of the key (even if the reference points to a value) */ - private final int hash; - - public WeakRef(final int hash, final T r, final ReferenceQueue q) { - super(r, q); - this.hash = hash; - } - - @Override - public int hashCode() { - return hash; - } - } - - //----------------------------------------------------------------------- - /** - * Replaces the superclass method to store the state of this class. - *

- * Serialization is not one of the JDK's nicest topics. Normal serialization will - * initialise the superclass before the subclass. Sometimes however, this isn't - * what you want, as in this case the put() method on read can be - * affected by subclass state. - *

- * The solution adopted here is to serialize the state data of this class in - * this protected method. This method must be called by the - * writeObject() of the first serializable subclass. - *

- * Subclasses may override if they have a specific field that must be present - * on read before this implementation will work. Generally, the read determines - * what must be serialized here, if anything. - * - * @param out the output stream - * @throws IOException if an error occurs while writing to the stream - */ - @Override - protected void doWriteObject(final ObjectOutputStream out) throws IOException { - out.writeInt(keyType.value); - out.writeInt(valueType.value); - out.writeBoolean(purgeValues); - out.writeFloat(loadFactor); - out.writeInt(data.length); - for (final MapIterator it = mapIterator(); it.hasNext();) { - out.writeObject(it.next()); - out.writeObject(it.getValue()); - } - out.writeObject(null); // null terminate map - // do not call super.doWriteObject() as code there doesn't work for reference map - } - - /** - * Replaces the superclass method to read the state of this class. - *

- * Serialization is not one of the JDK's nicest topics. Normal serialization will - * initialise the superclass before the subclass. Sometimes however, this isn't - * what you want, as in this case the put() method on read can be - * affected by subclass state. - *

- * The solution adopted here is to deserialize the state data of this class in - * this protected method. This method must be called by the - * readObject() of the first serializable subclass. - *

- * Subclasses may override if the subclass has a specific field that must be present - * before put() or calculateThreshold() will work correctly. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - @Override - @SuppressWarnings("unchecked") - protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - this.keyType = ReferenceStrength.resolve(in.readInt()); - this.valueType = ReferenceStrength.resolve(in.readInt()); - this.purgeValues = in.readBoolean(); - this.loadFactor = in.readFloat(); - final int capacity = in.readInt(); - init(); - data = new HashEntry[capacity]; - - // COLLECTIONS-599: Calculate threshold before populating, otherwise it will be 0 - // when it hits AbstractHashedMap.checkCapacity() and so will unnecessarily - // double up the size of the "data" array during population. - // - // NB: AbstractHashedMap.doReadObject() DOES calculate the threshold before populating. - // - threshold = calculateThreshold(data.length, loadFactor); - - while (true) { - final K key = (K) in.readObject(); - if (key == null) { - break; - } - final V value = (V) in.readObject(); - put(key, value); - } - // do not call super.doReadObject() as code there doesn't work for reference map - } - - /** - * Provided protected read-only access to the key type. - * @param type the type to check against. - * @return true if keyType has the specified type - */ - protected boolean isKeyType(final ReferenceStrength type) { - return this.keyType == type; - } - - /** - * Provided protected read-only access to the value type. - * @param type the type to check against. - * @return true if valueType has the specified type - */ - protected boolean isValueType(final ReferenceStrength type) { - return this.valueType == type; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * An abstract implementation of a hash-based map that allows the entries to + * be removed by the garbage collector. + *

+ * This class implements all the features necessary for a subclass reference + * hash-based map. Key-value entries are stored in instances of the + * ReferenceEntry class which can be overridden and replaced. + * The iterators can similarly be replaced, without the need to replace the KeySet, + * EntrySet and Values view classes. + *

+ *

+ * Overridable methods are provided to change the default hashing behaviour, and + * to change how entries are added to and removed from the map. Hopefully, all you + * need for unusual subclasses is here. + *

+ *

+ * When you construct an AbstractReferenceMap, you can specify what + * kind of references are used to store the map's keys and values. + * If non-hard references are used, then the garbage collector can remove + * mappings if a key or value becomes unreachable, or if the JVM's memory is + * running low. For information on how the different reference types behave, + * see {@link Reference}. + *

+ *

+ * Different types of references can be specified for keys and values. + * The keys can be configured to be weak but the values hard, + * in which case this class will behave like a + * + * WeakHashMap. However, you can also specify hard keys and + * weak values, or any other combination. The default constructor uses + * hard keys and soft values, providing a memory-sensitive cache. + *

+ *

+ * This {@link Map} implementation does not allow null elements. + * Attempting to add a null key or value to the map will raise a + * NullPointerException. + *

+ *

+ * All the available iterators can be reset back to the start by casting to + * ResettableIterator and calling reset(). + *

+ *

+ * This implementation is not synchronized. + * You can use {@link java.util.Collections#synchronizedMap} to + * provide synchronized access to a ReferenceMap. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * + * @see Reference + * @since 3.1 (extracted from ReferenceMap in 3.0) + */ +public abstract class AbstractReferenceMap extends AbstractHashedMap { + + /** + * Reference type enum. + */ + public enum ReferenceStrength { + HARD(0), SOFT(1), WEAK(2); + + /** value */ + public final int value; + + /** + * Resolve enum from int. + * @param value the int value + * @return ReferenceType + * @throws IllegalArgumentException if the specified value is invalid. + */ + public static ReferenceStrength resolve(final int value) { + switch (value) { + case 0: + return HARD; + case 1: + return SOFT; + case 2: + return WEAK; + default: + throw new IllegalArgumentException(); + } + } + + ReferenceStrength(final int value) { + this.value = value; + } + + } + + /** + * The reference type for keys. + */ + private ReferenceStrength keyType; + + /** + * The reference type for values. + */ + private ReferenceStrength valueType; + + /** + * Should the value be automatically purged when the associated key has been collected? + */ + private boolean purgeValues; + + /** + * ReferenceQueue used to eliminate stale mappings. + * See purge. + */ + private transient ReferenceQueue queue; + + //----------------------------------------------------------------------- + /** + * Constructor used during deserialization. + */ + protected AbstractReferenceMap() { + super(); + } + + /** + * Constructs a new empty map with the specified reference types, + * load factor and initial capacity. + * + * @param keyType the type of reference to use for keys; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param valueType the type of reference to use for values; + * must be {@link ReferenceStrength#HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param capacity the initial capacity for the map + * @param loadFactor the load factor for the map + * @param purgeValues should the value be automatically purged when the + * key is garbage collected + */ + protected AbstractReferenceMap( + final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity, + final float loadFactor, final boolean purgeValues) { + super(capacity, loadFactor); + this.keyType = keyType; + this.valueType = valueType; + this.purgeValues = purgeValues; + } + + /** + * Initialise this subclass during construction, cloning or deserialization. + */ + @Override + protected void init() { + queue = new ReferenceQueue<>(); + } + + //----------------------------------------------------------------------- + /** + * Gets the size of the map. + * + * @return the size + */ + @Override + public int size() { + purgeBeforeRead(); + return super.size(); + } + + /** + * Checks whether the map is currently empty. + * + * @return true if the map is currently size zero + */ + @Override + public boolean isEmpty() { + purgeBeforeRead(); + return super.isEmpty(); + } + + /** + * Checks whether the map contains the specified key. + * + * @param key the key to search for + * @return true if the map contains the key + */ + @Override + public boolean containsKey(final Object key) { + purgeBeforeRead(); + final Entry entry = getEntry(key); + if (entry == null) { + return false; + } + return entry.getValue() != null; + } + + /** + * Checks whether the map contains the specified value. + * + * @param value the value to search for + * @return true if the map contains the value + */ + @Override + public boolean containsValue(final Object value) { + purgeBeforeRead(); + if (value == null) { + return false; + } + return super.containsValue(value); + } + + /** + * Gets the value mapped to the key specified. + * + * @param key the key + * @return the mapped value, null if no match + */ + @Override + public V get(final Object key) { + purgeBeforeRead(); + final Entry entry = getEntry(key); + if (entry == null) { + return null; + } + return entry.getValue(); + } + + + /** + * Puts a key-value mapping into this map. + * Neither the key nor the value may be null. + * + * @param key the key to add, must not be null + * @param value the value to add, must not be null + * @return the value previously mapped to this key, null if none + * @throws NullPointerException if either the key or value is null + */ + @Override + public V put(final K key, final V value) { + if (key == null) { + throw new NullPointerException("null keys not allowed"); + } + if (value == null) { + throw new NullPointerException("null values not allowed"); + } + + purgeBeforeWrite(); + return super.put(key, value); + } + + /** + * Removes the specified mapping from this map. + * + * @param key the mapping to remove + * @return the value mapped to the removed key, null if key not in map + */ + @Override + public V remove(final Object key) { + if (key == null) { + return null; + } + purgeBeforeWrite(); + return super.remove(key); + } + + /** + * Clears this map. + */ + @Override + public void clear() { + super.clear(); + // drain the queue + while (queue.poll() != null) { + // empty + } + } + + //----------------------------------------------------------------------- + /** + * Gets a MapIterator over the reference map. + * The iterator only returns valid key/value pairs. + * + * @return a map iterator + */ + @Override + public MapIterator mapIterator() { + return new ReferenceMapIterator<>(this); + } + + /** + * Returns a set view of this map's entries. + * An iterator returned entry is valid until next() is called again. + * The setValue() method on the toArray entries has no effect. + * + * @return a set view of this map's entries + */ + @Override + public Set> entrySet() { + if (entrySet == null) { + entrySet = new ReferenceEntrySet<>(this); + } + return entrySet; + } + + /** + * Returns a set view of this map's keys. + * + * @return a set view of this map's keys + */ + @Override + public Set keySet() { + if (keySet == null) { + keySet = new ReferenceKeySet<>(this); + } + return keySet; + } + + /** + * Returns a collection view of this map's values. + * + * @return a set view of this map's values + */ + @Override + public Collection values() { + if (values == null) { + values = new ReferenceValues<>(this); + } + return values; + } + + //----------------------------------------------------------------------- + /** + * Purges stale mappings from this map before read operations. + *

+ * This implementation calls {@link #purge()} to maintain a consistent state. + */ + protected void purgeBeforeRead() { + purge(); + } + + /** + * Purges stale mappings from this map before write operations. + *

+ * This implementation calls {@link #purge()} to maintain a consistent state. + */ + protected void purgeBeforeWrite() { + purge(); + } + + /** + * Purges stale mappings from this map. + *

+ * Note that this method is not synchronized! Special + * care must be taken if, for instance, you want stale + * mappings to be removed on a periodic basis by some + * background thread. + */ + protected void purge() { + Reference ref = queue.poll(); + while (ref != null) { + purge(ref); + ref = queue.poll(); + } + } + + /** + * Purges the specified reference. + * + * @param ref the reference to purge + */ + protected void purge(final Reference ref) { + // The hashCode of the reference is the hashCode of the + // mapping key, even if the reference refers to the + // mapping value... + final int hash = ref.hashCode(); + final int index = hashIndex(hash, data.length); + HashEntry previous = null; + HashEntry entry = data[index]; + while (entry != null) { + ReferenceEntry refEntry = (ReferenceEntry) entry; + if (refEntry.purge(ref)) { + if (previous == null) { + data[index] = entry.next; + } else { + previous.next = entry.next; + } + this.size--; + refEntry.onPurge(); + return; + } + previous = entry; + entry = entry.next; + } + + } + + //----------------------------------------------------------------------- + /** + * Gets the entry mapped to the key specified. + * + * @param key the key + * @return the entry, null if no match + */ + @Override + protected HashEntry getEntry(final Object key) { + if (key == null) { + return null; + } + return super.getEntry(key); + } + + /** + * Gets the hash code for a MapEntry. + * Subclasses can override this, for example to use the identityHashCode. + * + * @param key the key to get a hash code for, may be null + * @param value the value to get a hash code for, may be null + * @return the hash code, as per the MapEntry specification + */ + protected int hashEntry(final Object key, final Object value) { + return (key == null ? 0 : key.hashCode()) ^ + (value == null ? 0 : value.hashCode()); + } + + /** + * Compares two keys, in internal converted form, to see if they are equal. + *

+ * This implementation converts the key from the entry to a real reference + * before comparison. + * + * @param key1 the first key to compare passed in from outside + * @param key2 the second key extracted from the entry via entry.key + * @return true if equal + */ + @Override + @SuppressWarnings("unchecked") + protected boolean isEqualKey(final Object key1, Object key2) { + key2 = keyType == ReferenceStrength.HARD ? key2 : ((Reference) key2).get(); + return key1 == key2 || key1.equals(key2); + } + + /** + * Creates a ReferenceEntry instead of a HashEntry. + * + * @param next the next entry in sequence + * @param hashCode the hash code to use + * @param key the key to store + * @param value the value to store + * @return the newly created entry + */ + @Override + protected ReferenceEntry createEntry(final HashEntry next, final int hashCode, + final K key, final V value) { + return new ReferenceEntry<>(this, next, hashCode, key, value); + } + + /** + * Creates an entry set iterator. + * + * @return the entrySet iterator + */ + @Override + protected Iterator> createEntrySetIterator() { + return new ReferenceEntrySetIterator<>(this); + } + + /** + * Creates an key set iterator. + * + * @return the keySet iterator + */ + @Override + protected Iterator createKeySetIterator() { + return new ReferenceKeySetIterator<>(this); + } + + /** + * Creates an values iterator. + * + * @return the values iterator + */ + @Override + protected Iterator createValuesIterator() { + return new ReferenceValuesIterator<>(this); + } + + //----------------------------------------------------------------------- + /** + * EntrySet implementation. + */ + static class ReferenceEntrySet extends EntrySet { + + protected ReferenceEntrySet(final AbstractHashedMap parent) { + super(parent); + } + + @Override + public Object[] toArray() { + return toArray(new Object[size()]); + } + + @Override + public T[] toArray(final T[] arr) { + // special implementation to handle disappearing entries + final ArrayList> list = new ArrayList<>(size()); + for (final Entry entry : this) { + list.add(new DefaultMapEntry<>(entry)); + } + return list.toArray(arr); + } + } + + //----------------------------------------------------------------------- + /** + * KeySet implementation. + */ + static class ReferenceKeySet extends KeySet { + + protected ReferenceKeySet(final AbstractHashedMap parent) { + super(parent); + } + + @Override + public Object[] toArray() { + return toArray(new Object[size()]); + } + + @Override + public T[] toArray(final T[] arr) { + // special implementation to handle disappearing keys + final List list = new ArrayList<>(size()); + for (final K key : this) { + list.add(key); + } + return list.toArray(arr); + } + } + + //----------------------------------------------------------------------- + /** + * Values implementation. + */ + static class ReferenceValues extends Values { + + protected ReferenceValues(final AbstractHashedMap parent) { + super(parent); + } + + @Override + public Object[] toArray() { + return toArray(new Object[size()]); + } + + @Override + public T[] toArray(final T[] arr) { + // special implementation to handle disappearing values + final List list = new ArrayList<>(size()); + for (final V value : this) { + list.add(value); + } + return list.toArray(arr); + } + } + + //----------------------------------------------------------------------- + /** + * A MapEntry implementation for the map. + *

+ * If getKey() or getValue() returns null, it means + * the mapping is stale and should be removed. + * + * @since 3.1 + */ + protected static class ReferenceEntry extends HashEntry { + /** The parent map */ + private final AbstractReferenceMap parent; + + /** + * Creates a new entry object for the ReferenceMap. + * + * @param parent the parent map + * @param next the next entry in the hash bucket + * @param hashCode the hash code of the key + * @param key the key + * @param value the value + */ + public ReferenceEntry(final AbstractReferenceMap parent, final HashEntry next, + final int hashCode, final K key, final V value) { + super(next, hashCode, null, null); + this.parent = parent; + this.key = toReference(parent.keyType, key, hashCode); + this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately + } + + /** + * Gets the key from the entry. + * This method dereferences weak and soft keys and thus may return null. + * + * @return the key, which may be null if it was garbage collected + */ + @Override + @SuppressWarnings("unchecked") + public K getKey() { + return (K) (parent.keyType == ReferenceStrength.HARD ? key : ((Reference) key).get()); + } + + /** + * Gets the value from the entry. + * This method dereferences weak and soft value and thus may return null. + * + * @return the value, which may be null if it was garbage collected + */ + @Override + @SuppressWarnings("unchecked") + public V getValue() { + return (V) (parent.valueType == ReferenceStrength.HARD ? value : ((Reference) value).get()); + } + + /** + * Sets the value of the entry. + * + * @param obj the object to store + * @return the previous value + */ + @Override + @SuppressWarnings("unchecked") + public V setValue(final V obj) { + final V old = getValue(); + if (parent.valueType != ReferenceStrength.HARD) { + ((Reference) value).clear(); + } + value = toReference(parent.valueType, obj, hashCode); + return old; + } + + /** + * Compares this map entry to another. + *

+ * This implementation uses isEqualKey and + * isEqualValue on the main map for comparison. + * + * @param obj the other map entry to compare to + * @return true if equal, false if not + */ + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Map.Entry)) { + return false; + } + + final Entry entry = (Entry)obj; + final Object entryKey = entry.getKey(); // convert to hard reference + final Object entryValue = entry.getValue(); // convert to hard reference + if (entryKey == null || entryValue == null) { + return false; + } + // compare using map methods, aiding identity subclass + // note that key is direct access and value is via method + return parent.isEqualKey(entryKey, key) && + parent.isEqualValue(entryValue, getValue()); + } + + /** + * Gets the hashcode of the entry using temporary hard references. + *

+ * This implementation uses hashEntry on the main map. + * + * @return the hashcode of the entry + */ + @Override + public int hashCode() { + return parent.hashEntry(getKey(), getValue()); + } + + /** + * Constructs a reference of the given type to the given referent. + * The reference is registered with the queue for later purging. + * + * @param the type of the referenced object + * @param type HARD, SOFT or WEAK + * @param referent the object to refer to + * @param hash the hash code of the key of the mapping; + * this number might be different from referent.hashCode() if + * the referent represents a value and not a key + * @return the reference to the object + */ + protected Object toReference(final ReferenceStrength type, final T referent, final int hash) { + if (type == ReferenceStrength.HARD) { + return referent; + } + if (type == ReferenceStrength.SOFT) { + return new SoftRef<>(hash, referent, parent.queue); + } + if (type == ReferenceStrength.WEAK) { + return new WeakRef<>(hash, referent, parent.queue); + } + throw new Error(); + } + + /** + * This is the callback for custom "after purge" logic + */ + protected void onPurge() { + // empty + } + + /** + * Purges the specified reference + * @param ref the reference to purge + * @return true or false + */ + protected boolean purge(final Reference ref) { + boolean r = parent.keyType != ReferenceStrength.HARD && key == ref; + r = r || parent.valueType != ReferenceStrength.HARD && value == ref; + if (r) { + if (parent.keyType != ReferenceStrength.HARD) { + ((Reference) key).clear(); + } + if (parent.valueType != ReferenceStrength.HARD) { + ((Reference) value).clear(); + } else if (parent.purgeValues) { + nullValue(); + } + } + return r; + } + + /** + * Gets the next entry in the bucket. + * + * @return the next entry in the bucket + */ + protected ReferenceEntry next() { + return (ReferenceEntry) next; + } + + /** + * This method can be overriden to provide custom logic to purge value + */ + protected void nullValue() { + value = null; + } + } + + //----------------------------------------------------------------------- + /** + * Base iterator class. + */ + static class ReferenceBaseIterator { + /** The parent map */ + final AbstractReferenceMap parent; + + // These fields keep track of where we are in the table. + int index; + ReferenceEntry entry; + ReferenceEntry previous; + + // These Object fields provide hard references to the + // current and next entry; this assures that if hasNext() + // returns true, next() will actually return a valid element. + K currentKey, nextKey; + V currentValue, nextValue; + + int expectedModCount; + + public ReferenceBaseIterator(final AbstractReferenceMap parent) { + super(); + this.parent = parent; + index = parent.size() != 0 ? parent.data.length : 0; + // have to do this here! size() invocation above + // may have altered the modCount. + expectedModCount = parent.modCount; + } + + public boolean hasNext() { + checkMod(); + while (nextNull()) { + ReferenceEntry e = entry; + int i = index; + while (e == null && i > 0) { + i--; + e = (ReferenceEntry) parent.data[i]; + } + entry = e; + index = i; + if (e == null) { + currentKey = null; + currentValue = null; + return false; + } + nextKey = e.getKey(); + nextValue = e.getValue(); + if (nextNull()) { + entry = entry.next(); + } + } + return true; + } + + private void checkMod() { + if (parent.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + private boolean nextNull() { + return nextKey == null || nextValue == null; + } + + protected ReferenceEntry nextEntry() { + checkMod(); + if (nextNull() && !hasNext()) { + throw new NoSuchElementException(); + } + previous = entry; + entry = entry.next(); + currentKey = nextKey; + currentValue = nextValue; + nextKey = null; + nextValue = null; + return previous; + } + + protected ReferenceEntry currentEntry() { + checkMod(); + return previous; + } + + public void remove() { + checkMod(); + if (previous == null) { + throw new IllegalStateException(); + } + parent.remove(currentKey); + previous = null; + currentKey = null; + currentValue = null; + expectedModCount = parent.modCount; + } + } + + /** + * The EntrySet iterator. + */ + static class ReferenceEntrySetIterator + extends ReferenceBaseIterator implements Iterator> { + + public ReferenceEntrySetIterator(final AbstractReferenceMap parent) { + super(parent); + } + + @Override + public Entry next() { + return nextEntry(); + } + + } + + /** + * The keySet iterator. + */ + static class ReferenceKeySetIterator extends ReferenceBaseIterator implements Iterator { + + @SuppressWarnings("unchecked") + ReferenceKeySetIterator(final AbstractReferenceMap parent) { + super((AbstractReferenceMap) parent); + } + + @Override + public K next() { + return nextEntry().getKey(); + } + } + + /** + * The values iterator. + */ + static class ReferenceValuesIterator extends ReferenceBaseIterator implements Iterator { + + @SuppressWarnings("unchecked") + ReferenceValuesIterator(final AbstractReferenceMap parent) { + super((AbstractReferenceMap) parent); + } + + @Override + public V next() { + return nextEntry().getValue(); + } + } + + /** + * The MapIterator implementation. + */ + static class ReferenceMapIterator extends ReferenceBaseIterator implements MapIterator { + + protected ReferenceMapIterator(final AbstractReferenceMap parent) { + super(parent); + } + + @Override + public K next() { + return nextEntry().getKey(); + } + + @Override + public K getKey() { + final HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); + } + return current.getKey(); + } + + @Override + public V getValue() { + final HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); + } + return current.getValue(); + } + + @Override + public V setValue(final V value) { + final HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); + } + return current.setValue(value); + } + } + + //----------------------------------------------------------------------- + // These two classes store the hashCode of the key of + // of the mapping, so that after they're dequeued a quick + // lookup of the bucket in the table can occur. + + /** + * A soft reference holder. + */ + static class SoftRef extends SoftReference { + /** the hashCode of the key (even if the reference points to a value) */ + private final int hash; + + public SoftRef(final int hash, final T r, final ReferenceQueue q) { + super(r, q); + this.hash = hash; + } + + @Override + public int hashCode() { + return hash; + } + } + + /** + * A weak reference holder. + */ + static class WeakRef extends WeakReference { + /** the hashCode of the key (even if the reference points to a value) */ + private final int hash; + + public WeakRef(final int hash, final T r, final ReferenceQueue q) { + super(r, q); + this.hash = hash; + } + + @Override + public int hashCode() { + return hash; + } + } + + //----------------------------------------------------------------------- + /** + * Replaces the superclass method to store the state of this class. + *

+ * Serialization is not one of the JDK's nicest topics. Normal serialization will + * initialise the superclass before the subclass. Sometimes however, this isn't + * what you want, as in this case the put() method on read can be + * affected by subclass state. + *

+ * The solution adopted here is to serialize the state data of this class in + * this protected method. This method must be called by the + * writeObject() of the first serializable subclass. + *

+ * Subclasses may override if they have a specific field that must be present + * on read before this implementation will work. Generally, the read determines + * what must be serialized here, if anything. + * + * @param out the output stream + * @throws IOException if an error occurs while writing to the stream + */ + @Override + protected void doWriteObject(final ObjectOutputStream out) throws IOException { + out.writeInt(keyType.value); + out.writeInt(valueType.value); + out.writeBoolean(purgeValues); + out.writeFloat(loadFactor); + out.writeInt(data.length); + for (final MapIterator it = mapIterator(); it.hasNext();) { + out.writeObject(it.next()); + out.writeObject(it.getValue()); + } + out.writeObject(null); // null terminate map + // do not call super.doWriteObject() as code there doesn't work for reference map + } + + /** + * Replaces the superclass method to read the state of this class. + *

+ * Serialization is not one of the JDK's nicest topics. Normal serialization will + * initialise the superclass before the subclass. Sometimes however, this isn't + * what you want, as in this case the put() method on read can be + * affected by subclass state. + *

+ * The solution adopted here is to deserialize the state data of this class in + * this protected method. This method must be called by the + * readObject() of the first serializable subclass. + *

+ * Subclasses may override if the subclass has a specific field that must be present + * before put() or calculateThreshold() will work correctly. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + @Override + @SuppressWarnings("unchecked") + protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + this.keyType = ReferenceStrength.resolve(in.readInt()); + this.valueType = ReferenceStrength.resolve(in.readInt()); + this.purgeValues = in.readBoolean(); + this.loadFactor = in.readFloat(); + final int capacity = in.readInt(); + init(); + data = new HashEntry[capacity]; + + // COLLECTIONS-599: Calculate threshold before populating, otherwise it will be 0 + // when it hits AbstractHashedMap.checkCapacity() and so will unnecessarily + // double up the size of the "data" array during population. + // + // NB: AbstractHashedMap.doReadObject() DOES calculate the threshold before populating. + // + threshold = calculateThreshold(data.length, loadFactor); + + while (true) { + final K key = (K) in.readObject(); + if (key == null) { + break; + } + final V value = (V) in.readObject(); + put(key, value); + } + // do not call super.doReadObject() as code there doesn't work for reference map + } + + /** + * Provided protected read-only access to the key type. + * @param type the type to check against. + * @return true if keyType has the specified type + */ + protected boolean isKeyType(final ReferenceStrength type) { + return this.keyType == type; + } + + /** + * Provided protected read-only access to the value type. + * @param type the type to check against. + * @return true if valueType has the specified type + */ + protected boolean isValueType(final ReferenceStrength type) { + return this.valueType == type; + } +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSerializableListDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSerializableListDecorator.java index 3775575579..cdf6abb067 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSerializableListDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSerializableListDecorator.java @@ -1,71 +1,71 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Collection; -import java.util.List; - -/** - * Serializable subclass of AbstractListDecorator. - * - * @since 3.1 - */ -public abstract class AbstractSerializableListDecorator - extends AbstractListDecorator { - - /** Serialization version */ - private static final long serialVersionUID = 2684959196747496299L; - - /** - * Constructor that wraps (not copies). - * - * @param list the list to decorate, must not be null - * @throws NullPointerException if list is null - */ - protected AbstractSerializableListDecorator(final List list) { - super(list); - } - - //----------------------------------------------------------------------- - /** - * Write the list out using a custom routine. - * - * @param out the output stream - * @throws IOException if an error occurs while writing to the stream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(decorated()); - } - - /** - * Read the list in using a custom routine. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - @SuppressWarnings("unchecked") - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - setCollection((Collection) in.readObject()); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.List; + +/** + * Serializable subclass of AbstractListDecorator. + * + * @since 3.1 + */ +public abstract class AbstractSerializableListDecorator + extends AbstractListDecorator { + + /** Serialization version */ + private static final long serialVersionUID = 2684959196747496299L; + + /** + * Constructor that wraps (not copies). + * + * @param list the list to decorate, must not be null + * @throws NullPointerException if list is null + */ + protected AbstractSerializableListDecorator(final List list) { + super(list); + } + + //----------------------------------------------------------------------- + /** + * Write the list out using a custom routine. + * + * @param out the output stream + * @throws IOException if an error occurs while writing to the stream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(decorated()); + } + + /** + * Read the list in using a custom routine. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + @SuppressWarnings("unchecked") + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + setCollection((Collection) in.readObject()); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSerializableSetDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSerializableSetDecorator.java index 9afd551b76..df209da34e 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSerializableSetDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSerializableSetDecorator.java @@ -1,72 +1,72 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Collection; -import java.util.Set; - -/** - * Serializable subclass of AbstractSetDecorator. - * - * @param the type of the elements in this set - * @since 3.1 - */ -public abstract class AbstractSerializableSetDecorator - extends AbstractSetDecorator { - - /** Serialization version */ - private static final long serialVersionUID = 1229469966212206107L; - - /** - * Constructor. - * - * @param set the list to decorate, must not be null - * @throws NullPointerException if set is null - */ - protected AbstractSerializableSetDecorator(final Set set) { - super(set); - } - - //----------------------------------------------------------------------- - /** - * Write the set out using a custom routine. - * - * @param out the output stream - * @throws IOException if an error occurs while writing to the stream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(decorated()); - } - - /** - * Read the set in using a custom routine. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - @SuppressWarnings("unchecked") - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - setCollection((Collection) in.readObject()); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.Set; + +/** + * Serializable subclass of AbstractSetDecorator. + * + * @param the type of the elements in this set + * @since 3.1 + */ +public abstract class AbstractSerializableSetDecorator + extends AbstractSetDecorator { + + /** Serialization version */ + private static final long serialVersionUID = 1229469966212206107L; + + /** + * Constructor. + * + * @param set the list to decorate, must not be null + * @throws NullPointerException if set is null + */ + protected AbstractSerializableSetDecorator(final Set set) { + super(set); + } + + //----------------------------------------------------------------------- + /** + * Write the set out using a custom routine. + * + * @param out the output stream + * @throws IOException if an error occurs while writing to the stream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(decorated()); + } + + /** + * Read the set in using a custom routine. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + @SuppressWarnings("unchecked") + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + setCollection((Collection) in.readObject()); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSetDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSetDecorator.java index bcbfaad038..1c9f6110f5 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSetDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSetDecorator.java @@ -1,74 +1,74 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Set; - -/** - * Decorates another Set to provide additional behaviour. - *

- * Methods are forwarded directly to the decorated set. - *

- * - * @param the type of the elements in this set - * @since 3.0 - */ -public abstract class AbstractSetDecorator extends AbstractCollectionDecorator implements - Set { - - /** Serialization version */ - private static final long serialVersionUID = -4678668309576958546L; - - /** - * Constructor only used in deserialization, do not use otherwise. - * @since 3.1 - */ - protected AbstractSetDecorator() { - super(); - } - - /** - * Constructor that wraps (not copies). - * - * @param set the set to decorate, must not be null - * @throws NullPointerException if set is null - */ - protected AbstractSetDecorator(final Set set) { - super(set); - } - - /** - * Gets the set being decorated. - * - * @return the decorated set - */ - @Override - protected Set decorated() { - return (Set) super.decorated(); - } - - @Override - public boolean equals(final Object object) { - return object == this || decorated().equals(object); - } - - @Override - public int hashCode() { - return decorated().hashCode(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Set; + +/** + * Decorates another Set to provide additional behaviour. + *

+ * Methods are forwarded directly to the decorated set. + *

+ * + * @param the type of the elements in this set + * @since 3.0 + */ +public abstract class AbstractSetDecorator extends AbstractCollectionDecorator implements + Set { + + /** Serialization version */ + private static final long serialVersionUID = -4678668309576958546L; + + /** + * Constructor only used in deserialization, do not use otherwise. + * @since 3.1 + */ + protected AbstractSetDecorator() { + super(); + } + + /** + * Constructor that wraps (not copies). + * + * @param set the set to decorate, must not be null + * @throws NullPointerException if set is null + */ + protected AbstractSetDecorator(final Set set) { + super(set); + } + + /** + * Gets the set being decorated. + * + * @return the decorated set + */ + @Override + protected Set decorated() { + return (Set) super.decorated(); + } + + @Override + public boolean equals(final Object object) { + return object == this || decorated().equals(object); + } + + @Override + public int hashCode() { + return decorated().hashCode(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSortedMapDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSortedMapDecorator.java index da41b81605..34bd91a6cb 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSortedMapDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractSortedMapDecorator.java @@ -1,169 +1,169 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Comparator; -import java.util.Iterator; -import java.util.ListIterator; -import java.util.Set; -import java.util.SortedMap; - -/** - * Provides a base decorator that enables additional functionality to be added - * to a Map via decoration. - *

- * Methods are forwarded directly to the decorated map. - *

- *

- * This implementation does not perform any special processing with the map views. - * Instead it simply returns the set/collection from the wrapped map. This may be - * undesirable, for example if you are trying to write a validating implementation - * it would provide a loophole around the validation. - * But, you might want that loophole, so this class is kept simple. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * @since 3.0 - */ -public abstract class AbstractSortedMapDecorator extends AbstractMapDecorator implements - IterableSortedMap { - - /** - * Constructor only used in deserialization, do not use otherwise. - * @since 3.1 - */ - protected AbstractSortedMapDecorator() { - super(); - } - - /** - * Constructor that wraps (not copies). - * - * @param map the map to decorate, must not be null - * @throws NullPointerException if the map is null - */ - public AbstractSortedMapDecorator(final SortedMap map) { - super(map); - } - - /** - * Gets the map being decorated. - * - * @return the decorated map - */ - @Override - protected SortedMap decorated() { - return (SortedMap) super.decorated(); - } - - //----------------------------------------------------------------------- - @Override - public Comparator comparator() { - return decorated().comparator(); - } - - @Override - public K firstKey() { - return decorated().firstKey(); - } - - @Override - public K lastKey() { - return decorated().lastKey(); - } - - @Override - public SortedMap subMap(final K fromKey, final K toKey) { - return decorated().subMap(fromKey, toKey); - } - - @Override - public SortedMap headMap(final K toKey) { - return decorated().headMap(toKey); - } - - @Override - public SortedMap tailMap(final K fromKey) { - return decorated().tailMap(fromKey); - } - - @Override - public K previousKey(final K key) { - final SortedMap headMap = headMap(key); - return headMap.isEmpty() ? null : headMap.lastKey(); - } - - @Override - public K nextKey(final K key) { - final Iterator it = tailMap(key).keySet().iterator(); - it.next(); - return it.hasNext() ? it.next() : null; - } - - /** - * {@inheritDoc} - */ - @Override - public OrderedMapIterator mapIterator() { - return new SortedMapIterator<>(entrySet()); - } - - /** - * OrderedMapIterator implementation. - * - * @param the key type - * @param the value type - */ - protected static class SortedMapIterator extends EntrySetToMapIteratorAdapter - implements OrderedMapIterator { - - /** - * Create a new AbstractSortedMapDecorator.SortedMapIterator. - * @param entrySet the entrySet to iterate - */ - protected SortedMapIterator(final Set> entrySet) { - super(entrySet); - } - - /** - * {@inheritDoc} - */ - @Override - public synchronized void reset() { - super.reset(); - iterator = new ListIteratorWrapper<>(iterator); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasPrevious() { - return ((ListIterator>) iterator).hasPrevious(); - } - - /** - * {@inheritDoc} - */ - @Override - public K previous() { - entry = ((ListIterator>) iterator).previous(); - return getKey(); - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.ListIterator; +import java.util.Set; +import java.util.SortedMap; + +/** + * Provides a base decorator that enables additional functionality to be added + * to a Map via decoration. + *

+ * Methods are forwarded directly to the decorated map. + *

+ *

+ * This implementation does not perform any special processing with the map views. + * Instead it simply returns the set/collection from the wrapped map. This may be + * undesirable, for example if you are trying to write a validating implementation + * it would provide a loophole around the validation. + * But, you might want that loophole, so this class is kept simple. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * @since 3.0 + */ +public abstract class AbstractSortedMapDecorator extends AbstractMapDecorator implements + IterableSortedMap { + + /** + * Constructor only used in deserialization, do not use otherwise. + * @since 3.1 + */ + protected AbstractSortedMapDecorator() { + super(); + } + + /** + * Constructor that wraps (not copies). + * + * @param map the map to decorate, must not be null + * @throws NullPointerException if the map is null + */ + public AbstractSortedMapDecorator(final SortedMap map) { + super(map); + } + + /** + * Gets the map being decorated. + * + * @return the decorated map + */ + @Override + protected SortedMap decorated() { + return (SortedMap) super.decorated(); + } + + //----------------------------------------------------------------------- + @Override + public Comparator comparator() { + return decorated().comparator(); + } + + @Override + public K firstKey() { + return decorated().firstKey(); + } + + @Override + public K lastKey() { + return decorated().lastKey(); + } + + @Override + public SortedMap subMap(final K fromKey, final K toKey) { + return decorated().subMap(fromKey, toKey); + } + + @Override + public SortedMap headMap(final K toKey) { + return decorated().headMap(toKey); + } + + @Override + public SortedMap tailMap(final K fromKey) { + return decorated().tailMap(fromKey); + } + + @Override + public K previousKey(final K key) { + final SortedMap headMap = headMap(key); + return headMap.isEmpty() ? null : headMap.lastKey(); + } + + @Override + public K nextKey(final K key) { + final Iterator it = tailMap(key).keySet().iterator(); + it.next(); + return it.hasNext() ? it.next() : null; + } + + /** + * {@inheritDoc} + */ + @Override + public OrderedMapIterator mapIterator() { + return new SortedMapIterator<>(entrySet()); + } + + /** + * OrderedMapIterator implementation. + * + * @param the key type + * @param the value type + */ + protected static class SortedMapIterator extends EntrySetToMapIteratorAdapter + implements OrderedMapIterator { + + /** + * Create a new AbstractSortedMapDecorator.SortedMapIterator. + * @param entrySet the entrySet to iterate + */ + protected SortedMapIterator(final Set> entrySet) { + super(entrySet); + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void reset() { + super.reset(); + iterator = new ListIteratorWrapper<>(iterator); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasPrevious() { + return ((ListIterator>) iterator).hasPrevious(); + } + + /** + * {@inheritDoc} + */ + @Override + public K previous() { + entry = ((ListIterator>) iterator).previous(); + return getKey(); + } + } +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractUntypedIteratorDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractUntypedIteratorDecorator.java index 02b8d74bf0..99dfbab670 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractUntypedIteratorDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/AbstractUntypedIteratorDecorator.java @@ -1,67 +1,67 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Iterator; - -/** - * Provides basic behaviour for decorating an iterator with extra functionality - * without committing the generic type of the Iterator implementation. - *

- * All methods are forwarded to the decorated iterator. - * - * @since 4.0 - */ -public abstract class AbstractUntypedIteratorDecorator implements Iterator { - - /** The iterator being decorated */ - private final Iterator iterator; - - /** - * Create a new AbstractUntypedIteratorDecorator. - * - * @param iterator the iterator to decorate - * @throws NullPointerException if the iterator is null - */ - protected AbstractUntypedIteratorDecorator(final Iterator iterator) { - super(); - if (iterator == null) { - throw new NullPointerException("Iterator must not be null"); - } - this.iterator = iterator; - } - - /** - * Gets the iterator being decorated. - * - * @return the decorated iterator - */ - protected Iterator getIterator() { - return iterator; - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public void remove() { - iterator.remove(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Iterator; + +/** + * Provides basic behaviour for decorating an iterator with extra functionality + * without committing the generic type of the Iterator implementation. + *

+ * All methods are forwarded to the decorated iterator. + * + * @since 4.0 + */ +public abstract class AbstractUntypedIteratorDecorator implements Iterator { + + /** The iterator being decorated */ + private final Iterator iterator; + + /** + * Create a new AbstractUntypedIteratorDecorator. + * + * @param iterator the iterator to decorate + * @throws NullPointerException if the iterator is null + */ + protected AbstractUntypedIteratorDecorator(final Iterator iterator) { + super(); + if (iterator == null) { + throw new NullPointerException("Iterator must not be null"); + } + this.iterator = iterator; + } + + /** + * Gets the iterator being decorated. + * + * @return the decorated iterator + */ + protected Iterator getIterator() { + return iterator; + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public void remove() { + iterator.remove(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/BidiMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/BidiMap.java index b530c887a2..e31be934c5 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/BidiMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/BidiMap.java @@ -1,155 +1,155 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Set; - -/** - * Defines a map that allows bidirectional lookup between key and values. - *

- * This extended Map represents a mapping where a key may - * lookup a value and a value may lookup a key with equal ease. - * This interface extends Map and so may be used anywhere a map - * is required. The interface provides an inverse map view, enabling - * full access to both directions of the BidiMap. - *

- *

- * Implementations should allow a value to be looked up from a key and - * a key to be looked up from a value with equal performance. - *

- *

- * This map enforces the restriction that there is a 1:1 relation between - * keys and values, meaning that multiple keys cannot map to the same value. - * This is required so that "inverting" the map results in a map without - * duplicate keys. See the {@link #put} method description for more information. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * - * @since 3.0 - */ -public interface BidiMap extends IterableMap { - - /** - * Puts the key-value pair into the map, replacing any previous pair. - *

- * When adding a key-value pair, the value may already exist in the map - * against a different key. That mapping is removed, to ensure that the - * value only occurs once in the inverse map. - *

- *
-     *  BidiMap map1 = new DualHashBidiMap();
-     *  map.put("A","B");  // contains A mapped to B, as per Map
-     *  map.put("A","C");  // contains A mapped to C, as per Map
-     *
-     *  BidiMap map2 = new DualHashBidiMap();
-     *  map.put("A","B");  // contains A mapped to B, as per Map
-     *  map.put("C","B");  // contains C mapped to B, key A is removed
-     * 
- * - * @param key the key to store - * @param value the value to store - * @return the previous value mapped to this key - * - * @throws UnsupportedOperationException if the put method is not supported - * @throws ClassCastException (optional) if the map limits the type of the - * value and the specified value is inappropriate - * @throws IllegalArgumentException (optional) if the map limits the values - * in some way and the value was invalid - * @throws NullPointerException (optional) if the map limits the values to - * non-null and null was specified - */ - @Override - V put(K key, V value); - - /** - * Gets the key that is currently mapped to the specified value. - *

- * If the value is not contained in the map, null is returned. - *

- *

- * Implementations should seek to make this method perform equally as well - * as get(Object). - *

- * - * @param value the value to find the key for - * @return the mapped key, or null if not found - * - * @throws ClassCastException (optional) if the map limits the type of the - * value and the specified value is inappropriate - * @throws NullPointerException (optional) if the map limits the values to - * non-null and null was specified - */ - K getKey(Object value); - - /** - * Removes the key-value pair that is currently mapped to the specified - * value (optional operation). - *

- * If the value is not contained in the map, null is returned. - *

- *

- * Implementations should seek to make this method perform equally as well - * as remove(Object). - *

- * - * @param value the value to find the key-value pair for - * @return the key that was removed, null if nothing removed - * - * @throws ClassCastException (optional) if the map limits the type of the - * value and the specified value is inappropriate - * @throws NullPointerException (optional) if the map limits the values to - * non-null and null was specified - * @throws UnsupportedOperationException if this method is not supported - * by the implementation - */ - K removeValue(Object value); - - /** - * Gets a view of this map where the keys and values are reversed. - *

- * Changes to one map will be visible in the other and vice versa. - * This enables both directions of the map to be accessed as a Map. - *

- *

- * Implementations should seek to avoid creating a new object every time this - * method is called. See AbstractMap.values() etc. Calling this - * method on the inverse map should return the original. - *

- * - * @return an inverted bidirectional map - */ - BidiMap inverseBidiMap(); - - /** - * Returns a {@link Set} view of the values contained in this map. - * The set is backed by the map, so changes to the map are reflected - * in the set, and vice-versa. If the map is modified while an iteration - * over the set is in progress (except through the iterator's own - * {@code remove} operation), the results of the iteration are undefined. - * The set supports element removal, which removes the corresponding - * mapping from the map, via the {@code Iterator.remove}, - * {@code Collection.remove}, {@code removeAll}, - * {@code retainAll} and {@code clear} operations. It does not - * support the {@code add} or {@code addAll} operations. - * - * @return a set view of the values contained in this map - */ - @Override - Set values(); -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Set; + +/** + * Defines a map that allows bidirectional lookup between key and values. + *

+ * This extended Map represents a mapping where a key may + * lookup a value and a value may lookup a key with equal ease. + * This interface extends Map and so may be used anywhere a map + * is required. The interface provides an inverse map view, enabling + * full access to both directions of the BidiMap. + *

+ *

+ * Implementations should allow a value to be looked up from a key and + * a key to be looked up from a value with equal performance. + *

+ *

+ * This map enforces the restriction that there is a 1:1 relation between + * keys and values, meaning that multiple keys cannot map to the same value. + * This is required so that "inverting" the map results in a map without + * duplicate keys. See the {@link #put} method description for more information. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * + * @since 3.0 + */ +public interface BidiMap extends IterableMap { + + /** + * Puts the key-value pair into the map, replacing any previous pair. + *

+ * When adding a key-value pair, the value may already exist in the map + * against a different key. That mapping is removed, to ensure that the + * value only occurs once in the inverse map. + *

+ *
+     *  BidiMap map1 = new DualHashBidiMap();
+     *  map.put("A","B");  // contains A mapped to B, as per Map
+     *  map.put("A","C");  // contains A mapped to C, as per Map
+     *
+     *  BidiMap map2 = new DualHashBidiMap();
+     *  map.put("A","B");  // contains A mapped to B, as per Map
+     *  map.put("C","B");  // contains C mapped to B, key A is removed
+     * 
+ * + * @param key the key to store + * @param value the value to store + * @return the previous value mapped to this key + * + * @throws UnsupportedOperationException if the put method is not supported + * @throws ClassCastException (optional) if the map limits the type of the + * value and the specified value is inappropriate + * @throws IllegalArgumentException (optional) if the map limits the values + * in some way and the value was invalid + * @throws NullPointerException (optional) if the map limits the values to + * non-null and null was specified + */ + @Override + V put(K key, V value); + + /** + * Gets the key that is currently mapped to the specified value. + *

+ * If the value is not contained in the map, null is returned. + *

+ *

+ * Implementations should seek to make this method perform equally as well + * as get(Object). + *

+ * + * @param value the value to find the key for + * @return the mapped key, or null if not found + * + * @throws ClassCastException (optional) if the map limits the type of the + * value and the specified value is inappropriate + * @throws NullPointerException (optional) if the map limits the values to + * non-null and null was specified + */ + K getKey(Object value); + + /** + * Removes the key-value pair that is currently mapped to the specified + * value (optional operation). + *

+ * If the value is not contained in the map, null is returned. + *

+ *

+ * Implementations should seek to make this method perform equally as well + * as remove(Object). + *

+ * + * @param value the value to find the key-value pair for + * @return the key that was removed, null if nothing removed + * + * @throws ClassCastException (optional) if the map limits the type of the + * value and the specified value is inappropriate + * @throws NullPointerException (optional) if the map limits the values to + * non-null and null was specified + * @throws UnsupportedOperationException if this method is not supported + * by the implementation + */ + K removeValue(Object value); + + /** + * Gets a view of this map where the keys and values are reversed. + *

+ * Changes to one map will be visible in the other and vice versa. + * This enables both directions of the map to be accessed as a Map. + *

+ *

+ * Implementations should seek to avoid creating a new object every time this + * method is called. See AbstractMap.values() etc. Calling this + * method on the inverse map should return the original. + *

+ * + * @return an inverted bidirectional map + */ + BidiMap inverseBidiMap(); + + /** + * Returns a {@link Set} view of the values contained in this map. + * The set is backed by the map, so changes to the map are reflected + * in the set, and vice-versa. If the map is modified while an iteration + * over the set is in progress (except through the iterator's own + * {@code remove} operation), the results of the iteration are undefined. + * The set supports element removal, which removes the corresponding + * mapping from the map, via the {@code Iterator.remove}, + * {@code Collection.remove}, {@code removeAll}, + * {@code retainAll} and {@code clear} operations. It does not + * support the {@code add} or {@code addAll} operations. + * + * @return a set view of the values contained in this map + */ + @Override + Set values(); +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/BoundedMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/BoundedMap.java index cbafa552e9..1ec43e6287 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/BoundedMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/BoundedMap.java @@ -1,47 +1,47 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Defines a map that is bounded in size. - *

- * The size of the map can vary, but it can never exceed a preset - * maximum number of elements. This interface allows the querying of details - * associated with the maximum number of elements. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * @since 3.0 - */ -public interface BoundedMap extends IterableMap { - - /** - * Returns true if this map is full and no new elements can be added. - * - * @return true if the map is full - */ - boolean isFull(); - - /** - * Gets the maximum size of the map (the bound). - * - * @return the maximum number of elements the map can hold - */ - int maxSize(); - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Defines a map that is bounded in size. + *

+ * The size of the map can vary, but it can never exceed a preset + * maximum number of elements. This interface allows the querying of details + * associated with the maximum number of elements. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * @since 3.0 + */ +public interface BoundedMap extends IterableMap { + + /** + * Returns true if this map is full and no new elements can be added. + * + * @return true if the map is full + */ + boolean isFull(); + + /** + * Gets the maximum size of the map (the bound). + * + * @return the maximum number of elements the map can hold + */ + int maxSize(); + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ComparableComparator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ComparableComparator.java index 98be3d0e62..e694992917 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ComparableComparator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ComparableComparator.java @@ -1,131 +1,131 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.Serializable; -import java.util.Comparator; - -/** - * A {@link Comparator Comparator} that compares {@link Comparable Comparable} - * objects. - *

- * This Comparator is useful, for example, for enforcing the natural order in - * custom implementations of {@link java.util.SortedSet SortedSet} and - * {@link java.util.SortedMap SortedMap}. - *

- *

- * Note: In the 2.0 and 2.1 releases of Commons Collections, this class would - * throw a {@link ClassCastException} if either of the arguments to - * {@link #compare(Object, Object) compare} were null, not - * {@link Comparable Comparable}, or for which - * {@link Comparable#compareTo(Object) compareTo} gave inconsistent results. - * This is no longer the case. See {@link #compare(Object, Object) compare} for - * details. - *

- * - * @param the type of objects compared by this comparator - * - * @since 2.0 - * @see java.util.Collections#reverseOrder() - */ -public class ComparableComparator> implements Comparator, Serializable { - - /** Serialization version. */ - private static final long serialVersionUID=-291439688585137865L; - - /** The singleton instance. */ - @SuppressWarnings("rawtypes") - public static final ComparableComparator INSTANCE = new ComparableComparator(); - - //----------------------------------------------------------------------- - /** - * Gets the singleton instance of a ComparableComparator. - *

- * Developers are encouraged to use the comparator returned from this method - * instead of constructing a new instance to reduce allocation and GC overhead - * when multiple comparable comparators may be used in the same VM. - * - * @param the element type - * @return the singleton ComparableComparator - * @since 4.0 - */ - public static > ComparableComparator comparableComparator() { - return INSTANCE; - } - - //----------------------------------------------------------------------- - /** - * Constructor whose use should be avoided. - *

- * Please use the {@link #comparableComparator()} method whenever possible. - */ - public ComparableComparator() { - super(); - } - - //----------------------------------------------------------------------- - /** - * Compare the two {@link Comparable Comparable} arguments. - * This method is equivalent to: - *

((Comparable)obj1).compareTo(obj2)
- * - * @param obj1 the first object to compare - * @param obj2 the second object to compare - * @return negative if obj1 is less, positive if greater, zero if equal - * @throws NullPointerException if obj1 is null, - * or when ((Comparable)obj1).compareTo(obj2) does - * @throws ClassCastException if obj1 is not a Comparable, - * or when ((Comparable)obj1).compareTo(obj2) does - */ - @Override - public int compare(final E obj1, final E obj2) { - return obj1.compareTo(obj2); - } - - //----------------------------------------------------------------------- - /** - * Implement a hash code for this comparator that is consistent with - * {@link #equals(Object) equals}. - * - * @return a hash code for this comparator. - * @since 3.0 - */ - @Override - public int hashCode() { - return "ComparableComparator".hashCode(); - } - - /** - * Returns {@code true} iff that Object is is a {@link Comparator Comparator} - * whose ordering is known to be equivalent to mine. - *

- * This implementation returns {@code true} iff - * object.{@link Object#getClass() getClass()} equals - * this.getClass(). Subclasses may want to override this behavior to remain - * consistent with the {@link Comparator#equals(Object)} contract. - * - * @param object the object to compare with - * @return {@code true} if equal - * @since 3.0 - */ - @Override - public boolean equals(final Object object) { - return this == object || - null != object && object.getClass().equals(this.getClass()); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.Serializable; +import java.util.Comparator; + +/** + * A {@link Comparator Comparator} that compares {@link Comparable Comparable} + * objects. + *

+ * This Comparator is useful, for example, for enforcing the natural order in + * custom implementations of {@link java.util.SortedSet SortedSet} and + * {@link java.util.SortedMap SortedMap}. + *

+ *

+ * Note: In the 2.0 and 2.1 releases of Commons Collections, this class would + * throw a {@link ClassCastException} if either of the arguments to + * {@link #compare(Object, Object) compare} were null, not + * {@link Comparable Comparable}, or for which + * {@link Comparable#compareTo(Object) compareTo} gave inconsistent results. + * This is no longer the case. See {@link #compare(Object, Object) compare} for + * details. + *

+ * + * @param the type of objects compared by this comparator + * + * @since 2.0 + * @see java.util.Collections#reverseOrder() + */ +public class ComparableComparator> implements Comparator, Serializable { + + /** Serialization version. */ + private static final long serialVersionUID=-291439688585137865L; + + /** The singleton instance. */ + @SuppressWarnings("rawtypes") + public static final ComparableComparator INSTANCE = new ComparableComparator(); + + //----------------------------------------------------------------------- + /** + * Gets the singleton instance of a ComparableComparator. + *

+ * Developers are encouraged to use the comparator returned from this method + * instead of constructing a new instance to reduce allocation and GC overhead + * when multiple comparable comparators may be used in the same VM. + * + * @param the element type + * @return the singleton ComparableComparator + * @since 4.0 + */ + public static > ComparableComparator comparableComparator() { + return INSTANCE; + } + + //----------------------------------------------------------------------- + /** + * Constructor whose use should be avoided. + *

+ * Please use the {@link #comparableComparator()} method whenever possible. + */ + public ComparableComparator() { + super(); + } + + //----------------------------------------------------------------------- + /** + * Compare the two {@link Comparable Comparable} arguments. + * This method is equivalent to: + *

((Comparable)obj1).compareTo(obj2)
+ * + * @param obj1 the first object to compare + * @param obj2 the second object to compare + * @return negative if obj1 is less, positive if greater, zero if equal + * @throws NullPointerException if obj1 is null, + * or when ((Comparable)obj1).compareTo(obj2) does + * @throws ClassCastException if obj1 is not a Comparable, + * or when ((Comparable)obj1).compareTo(obj2) does + */ + @Override + public int compare(final E obj1, final E obj2) { + return obj1.compareTo(obj2); + } + + //----------------------------------------------------------------------- + /** + * Implement a hash code for this comparator that is consistent with + * {@link #equals(Object) equals}. + * + * @return a hash code for this comparator. + * @since 3.0 + */ + @Override + public int hashCode() { + return "ComparableComparator".hashCode(); + } + + /** + * Returns {@code true} iff that Object is is a {@link Comparator Comparator} + * whose ordering is known to be equivalent to mine. + *

+ * This implementation returns {@code true} iff + * object.{@link Object#getClass() getClass()} equals + * this.getClass(). Subclasses may want to override this behavior to remain + * consistent with the {@link Comparator#equals(Object)} contract. + * + * @param object the object to compare with + * @return {@code true} if equal + * @since 3.0 + */ + @Override + public boolean equals(final Object object) { + return this == object || + null != object && object.getClass().equals(this.getClass()); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ComparatorChain.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ComparatorChain.java index 78c65f03cc..dc15c5a4be 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ComparatorChain.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ComparatorChain.java @@ -1,352 +1,352 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -/** - * A ComparatorChain is a Comparator that wraps one or more Comparators in - * sequence. The ComparatorChain calls each Comparator in sequence until either - * 1) any single Comparator returns a non-zero result (and that result is then - * returned), or 2) the ComparatorChain is exhausted (and zero is returned). - * This type of sorting is very similar to multi-column sorting in SQL, and this - * class allows Java classes to emulate that kind of behaviour when sorting a - * List. - *

- * To further facilitate SQL-like sorting, the order of any single Comparator in - * the list can be reversed. - *

- *

- * Calling a method that adds new Comparators or changes the ascend/descend sort - * after compare(Object, Object) has been called will result in an - * UnsupportedOperationException. However, take care to not alter the - * underlying List of Comparators or the BitSet that defines the sort order. - *

- *

- * Instances of ComparatorChain are not synchronized. The class is not - * thread-safe at construction time, but it is thread-safe to perform - * multiple comparisons after all the setup operations are complete. - *

- * - * @param the type of objects compared by this comparator - * @since 2.0 - */ -public class ComparatorChain implements Comparator, Serializable { - - /** Serialization version from Collections 2.0. */ - private static final long serialVersionUID = -721644942746081630L; - - /** The list of comparators in the chain. */ - private final List> comparatorChain; - /** Order - false (clear) = ascend; true (set) = descend. */ - private BitSet orderingBits = null; - /** Whether the chain has been "locked". */ - private boolean isLocked = false; - - //----------------------------------------------------------------------- - /** - * Construct a ComparatorChain with no Comparators. - * You must add at least one Comparator before calling - * the compare(Object,Object) method, or an - * UnsupportedOperationException is thrown - */ - public ComparatorChain() { - this(new ArrayList<>(), new BitSet()); - } - - /** - * Construct a ComparatorChain with a single Comparator, - * sorting in the forward order - * - * @param comparator First comparator in the Comparator chain - */ - public ComparatorChain(final Comparator comparator) { - this(comparator, false); - } - - /** - * Construct a Comparator chain with a single Comparator, - * sorting in the given order - * - * @param comparator First Comparator in the ComparatorChain - * @param reverse false = forward sort; true = reverse sort - */ - public ComparatorChain(final Comparator comparator, final boolean reverse) { - comparatorChain = new ArrayList<>(1); - comparatorChain.add(comparator); - orderingBits = new BitSet(1); - if (reverse) { - orderingBits.set(0); - } - } - - /** - * Construct a ComparatorChain from the Comparators in the - * List. All Comparators will default to the forward - * sort order. - * - * @param list List of Comparators - * @see #ComparatorChain(List,BitSet) - */ - public ComparatorChain(final List> list) { - this(list, new BitSet(list.size())); - } - - /** - * Construct a ComparatorChain from the Comparators in the - * given List. The sort order of each column will be - * drawn from the given BitSet. When determining the sort - * order for Comparator at index i in the List, - * the ComparatorChain will call BitSet.get(i). - * If that method returns false, the forward - * sort order is used; a return value of true - * indicates reverse sort order. - * - * @param list List of Comparators. NOTE: This constructor does not perform a - * defensive copy of the list - * @param bits Sort order for each Comparator. Extra bits are ignored, - * unless extra Comparators are added by another method. - */ - public ComparatorChain(final List> list, final BitSet bits) { - comparatorChain = list; - orderingBits = bits; - } - - //----------------------------------------------------------------------- - /** - * Add a Comparator to the end of the chain using the - * forward sort order - * - * @param comparator Comparator with the forward sort order - */ - public void addComparator(final Comparator comparator) { - addComparator(comparator, false); - } - - /** - * Add a Comparator to the end of the chain using the - * given sort order - * - * @param comparator Comparator to add to the end of the chain - * @param reverse false = forward sort order; true = reverse sort order - */ - public void addComparator(final Comparator comparator, final boolean reverse) { - checkLocked(); - - comparatorChain.add(comparator); - if (reverse) { - orderingBits.set(comparatorChain.size() - 1); - } - } - - /** - * Replace the Comparator at the given index, maintaining - * the existing sort order. - * - * @param index index of the Comparator to replace - * @param comparator Comparator to place at the given index - * @throws IndexOutOfBoundsException - * if index < 0 or index >= size() - */ - public void setComparator(final int index, final Comparator comparator) throws IndexOutOfBoundsException { - setComparator(index, comparator, false); - } - - /** - * Replace the Comparator at the given index in the - * ComparatorChain, using the given sort order - * - * @param index index of the Comparator to replace - * @param comparator Comparator to set - * @param reverse false = forward sort order; true = reverse sort order - */ - public void setComparator(final int index, final Comparator comparator, final boolean reverse) { - checkLocked(); - - comparatorChain.set(index,comparator); - if (reverse) { - orderingBits.set(index); - } else { - orderingBits.clear(index); - } - } - - /** - * Change the sort order at the given index in the - * ComparatorChain to a forward sort. - * - * @param index Index of the ComparatorChain - */ - public void setForwardSort(final int index) { - checkLocked(); - orderingBits.clear(index); - } - - /** - * Change the sort order at the given index in the - * ComparatorChain to a reverse sort. - * - * @param index Index of the ComparatorChain - */ - public void setReverseSort(final int index) { - checkLocked(); - orderingBits.set(index); - } - - /** - * Number of Comparators in the current ComparatorChain. - * - * @return Comparator count - */ - public int size() { - return comparatorChain.size(); - } - - /** - * Determine if modifications can still be made to the - * ComparatorChain. ComparatorChains cannot be modified - * once they have performed a comparison. - * - * @return true = ComparatorChain cannot be modified; false = - * ComparatorChain can still be modified. - */ - public boolean isLocked() { - return isLocked; - } - - /** - * Throws an exception if the {@link ComparatorChain} is locked. - * - * @throws UnsupportedOperationException if the {@link ComparatorChain} is locked - */ - private void checkLocked() { - if (isLocked) { - throw new UnsupportedOperationException( - "Comparator ordering cannot be changed after the first comparison is performed"); - } - } - - /** - * Throws an exception if the {@link ComparatorChain} is empty. - * - * @throws UnsupportedOperationException if the {@link ComparatorChain} is empty - */ - private void checkChainIntegrity() { - if (comparatorChain.size() == 0) { - throw new UnsupportedOperationException("ComparatorChains must contain at least one Comparator"); - } - } - - //----------------------------------------------------------------------- - /** - * Perform comparisons on the Objects as per - * Comparator.compare(o1,o2). - * - * @param o1 the first object to compare - * @param o2 the second object to compare - * @return -1, 0, or 1 - * @throws UnsupportedOperationException if the ComparatorChain does not contain at least one Comparator - */ - @Override - public int compare(final E o1, final E o2) throws UnsupportedOperationException { - if (!isLocked) { - checkChainIntegrity(); - isLocked = true; - } - - // iterate over all comparators in the chain - final Iterator> comparators = comparatorChain.iterator(); - for (int comparatorIndex = 0; comparators.hasNext(); ++comparatorIndex) { - - final Comparator comparator = comparators.next(); - int retval = comparator.compare(o1,o2); - if (retval != 0) { - // invert the order if it is a reverse sort - if (orderingBits.get(comparatorIndex)) { - if (retval > 0) { - retval = -1; - } else { - retval = 1; - } - } - return retval; - } - } - - // if comparators are exhausted, return 0 - return 0; - } - - //----------------------------------------------------------------------- - /** - * Implement a hash code for this comparator that is consistent with - * {@link #equals(Object) equals}. - * - * @return a suitable hash code - * @since 3.0 - */ - @Override - public int hashCode() { - int hash = 0; - if (null != comparatorChain) { - hash ^= comparatorChain.hashCode(); - } - if (null != orderingBits) { - hash ^= orderingBits.hashCode(); - } - return hash; - } - - /** - * Returns true iff that Object is - * is a {@link Comparator} whose ordering is known to be - * equivalent to mine. - *

- * This implementation returns true - * iff object.{@link Object#getClass() getClass()} - * equals this.getClass(), and the underlying - * comparators and order bits are equal. - * Subclasses may want to override this behavior to remain consistent - * with the {@link Comparator#equals(Object)} contract. - * - * @param object the object to compare with - * @return true if equal - * @since 3.0 - */ - @Override - public boolean equals(final Object object) { - if (this == object) { - return true; - } - if (null == object) { - return false; - } - if (object.getClass().equals(this.getClass())) { - final ComparatorChain chain = (ComparatorChain) object; - return (null == orderingBits ? null == chain.orderingBits : orderingBits.equals(chain.orderingBits)) && - (null == comparatorChain ? null == chain.comparatorChain : - comparatorChain.equals(chain.comparatorChain)); - } - return false; - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +/** + * A ComparatorChain is a Comparator that wraps one or more Comparators in + * sequence. The ComparatorChain calls each Comparator in sequence until either + * 1) any single Comparator returns a non-zero result (and that result is then + * returned), or 2) the ComparatorChain is exhausted (and zero is returned). + * This type of sorting is very similar to multi-column sorting in SQL, and this + * class allows Java classes to emulate that kind of behaviour when sorting a + * List. + *

+ * To further facilitate SQL-like sorting, the order of any single Comparator in + * the list can be reversed. + *

+ *

+ * Calling a method that adds new Comparators or changes the ascend/descend sort + * after compare(Object, Object) has been called will result in an + * UnsupportedOperationException. However, take care to not alter the + * underlying List of Comparators or the BitSet that defines the sort order. + *

+ *

+ * Instances of ComparatorChain are not synchronized. The class is not + * thread-safe at construction time, but it is thread-safe to perform + * multiple comparisons after all the setup operations are complete. + *

+ * + * @param the type of objects compared by this comparator + * @since 2.0 + */ +public class ComparatorChain implements Comparator, Serializable { + + /** Serialization version from Collections 2.0. */ + private static final long serialVersionUID = -721644942746081630L; + + /** The list of comparators in the chain. */ + private final List> comparatorChain; + /** Order - false (clear) = ascend; true (set) = descend. */ + private BitSet orderingBits = null; + /** Whether the chain has been "locked". */ + private boolean isLocked = false; + + //----------------------------------------------------------------------- + /** + * Construct a ComparatorChain with no Comparators. + * You must add at least one Comparator before calling + * the compare(Object,Object) method, or an + * UnsupportedOperationException is thrown + */ + public ComparatorChain() { + this(new ArrayList<>(), new BitSet()); + } + + /** + * Construct a ComparatorChain with a single Comparator, + * sorting in the forward order + * + * @param comparator First comparator in the Comparator chain + */ + public ComparatorChain(final Comparator comparator) { + this(comparator, false); + } + + /** + * Construct a Comparator chain with a single Comparator, + * sorting in the given order + * + * @param comparator First Comparator in the ComparatorChain + * @param reverse false = forward sort; true = reverse sort + */ + public ComparatorChain(final Comparator comparator, final boolean reverse) { + comparatorChain = new ArrayList<>(1); + comparatorChain.add(comparator); + orderingBits = new BitSet(1); + if (reverse) { + orderingBits.set(0); + } + } + + /** + * Construct a ComparatorChain from the Comparators in the + * List. All Comparators will default to the forward + * sort order. + * + * @param list List of Comparators + * @see #ComparatorChain(List,BitSet) + */ + public ComparatorChain(final List> list) { + this(list, new BitSet(list.size())); + } + + /** + * Construct a ComparatorChain from the Comparators in the + * given List. The sort order of each column will be + * drawn from the given BitSet. When determining the sort + * order for Comparator at index i in the List, + * the ComparatorChain will call BitSet.get(i). + * If that method returns false, the forward + * sort order is used; a return value of true + * indicates reverse sort order. + * + * @param list List of Comparators. NOTE: This constructor does not perform a + * defensive copy of the list + * @param bits Sort order for each Comparator. Extra bits are ignored, + * unless extra Comparators are added by another method. + */ + public ComparatorChain(final List> list, final BitSet bits) { + comparatorChain = list; + orderingBits = bits; + } + + //----------------------------------------------------------------------- + /** + * Add a Comparator to the end of the chain using the + * forward sort order + * + * @param comparator Comparator with the forward sort order + */ + public void addComparator(final Comparator comparator) { + addComparator(comparator, false); + } + + /** + * Add a Comparator to the end of the chain using the + * given sort order + * + * @param comparator Comparator to add to the end of the chain + * @param reverse false = forward sort order; true = reverse sort order + */ + public void addComparator(final Comparator comparator, final boolean reverse) { + checkLocked(); + + comparatorChain.add(comparator); + if (reverse) { + orderingBits.set(comparatorChain.size() - 1); + } + } + + /** + * Replace the Comparator at the given index, maintaining + * the existing sort order. + * + * @param index index of the Comparator to replace + * @param comparator Comparator to place at the given index + * @throws IndexOutOfBoundsException + * if index < 0 or index >= size() + */ + public void setComparator(final int index, final Comparator comparator) throws IndexOutOfBoundsException { + setComparator(index, comparator, false); + } + + /** + * Replace the Comparator at the given index in the + * ComparatorChain, using the given sort order + * + * @param index index of the Comparator to replace + * @param comparator Comparator to set + * @param reverse false = forward sort order; true = reverse sort order + */ + public void setComparator(final int index, final Comparator comparator, final boolean reverse) { + checkLocked(); + + comparatorChain.set(index,comparator); + if (reverse) { + orderingBits.set(index); + } else { + orderingBits.clear(index); + } + } + + /** + * Change the sort order at the given index in the + * ComparatorChain to a forward sort. + * + * @param index Index of the ComparatorChain + */ + public void setForwardSort(final int index) { + checkLocked(); + orderingBits.clear(index); + } + + /** + * Change the sort order at the given index in the + * ComparatorChain to a reverse sort. + * + * @param index Index of the ComparatorChain + */ + public void setReverseSort(final int index) { + checkLocked(); + orderingBits.set(index); + } + + /** + * Number of Comparators in the current ComparatorChain. + * + * @return Comparator count + */ + public int size() { + return comparatorChain.size(); + } + + /** + * Determine if modifications can still be made to the + * ComparatorChain. ComparatorChains cannot be modified + * once they have performed a comparison. + * + * @return true = ComparatorChain cannot be modified; false = + * ComparatorChain can still be modified. + */ + public boolean isLocked() { + return isLocked; + } + + /** + * Throws an exception if the {@link ComparatorChain} is locked. + * + * @throws UnsupportedOperationException if the {@link ComparatorChain} is locked + */ + private void checkLocked() { + if (isLocked) { + throw new UnsupportedOperationException( + "Comparator ordering cannot be changed after the first comparison is performed"); + } + } + + /** + * Throws an exception if the {@link ComparatorChain} is empty. + * + * @throws UnsupportedOperationException if the {@link ComparatorChain} is empty + */ + private void checkChainIntegrity() { + if (comparatorChain.size() == 0) { + throw new UnsupportedOperationException("ComparatorChains must contain at least one Comparator"); + } + } + + //----------------------------------------------------------------------- + /** + * Perform comparisons on the Objects as per + * Comparator.compare(o1,o2). + * + * @param o1 the first object to compare + * @param o2 the second object to compare + * @return -1, 0, or 1 + * @throws UnsupportedOperationException if the ComparatorChain does not contain at least one Comparator + */ + @Override + public int compare(final E o1, final E o2) throws UnsupportedOperationException { + if (!isLocked) { + checkChainIntegrity(); + isLocked = true; + } + + // iterate over all comparators in the chain + final Iterator> comparators = comparatorChain.iterator(); + for (int comparatorIndex = 0; comparators.hasNext(); ++comparatorIndex) { + + final Comparator comparator = comparators.next(); + int retval = comparator.compare(o1,o2); + if (retval != 0) { + // invert the order if it is a reverse sort + if (orderingBits.get(comparatorIndex)) { + if (retval > 0) { + retval = -1; + } else { + retval = 1; + } + } + return retval; + } + } + + // if comparators are exhausted, return 0 + return 0; + } + + //----------------------------------------------------------------------- + /** + * Implement a hash code for this comparator that is consistent with + * {@link #equals(Object) equals}. + * + * @return a suitable hash code + * @since 3.0 + */ + @Override + public int hashCode() { + int hash = 0; + if (null != comparatorChain) { + hash ^= comparatorChain.hashCode(); + } + if (null != orderingBits) { + hash ^= orderingBits.hashCode(); + } + return hash; + } + + /** + * Returns true iff that Object is + * is a {@link Comparator} whose ordering is known to be + * equivalent to mine. + *

+ * This implementation returns true + * iff object.{@link Object#getClass() getClass()} + * equals this.getClass(), and the underlying + * comparators and order bits are equal. + * Subclasses may want to override this behavior to remain consistent + * with the {@link Comparator#equals(Object)} contract. + * + * @param object the object to compare with + * @return true if equal + * @since 3.0 + */ + @Override + public boolean equals(final Object object) { + if (this == object) { + return true; + } + if (null == object) { + return false; + } + if (object.getClass().equals(this.getClass())) { + final ComparatorChain chain = (ComparatorChain) object; + return (null == orderingBits ? null == chain.orderingBits : orderingBits.equals(chain.orderingBits)) && + (null == comparatorChain ? null == chain.comparatorChain : + comparatorChain.equals(chain.comparatorChain)); + } + return false; + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DefaultMapEntry.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DefaultMapEntry.java index 59aaf7209d..8076e31f62 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DefaultMapEntry.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DefaultMapEntry.java @@ -1,61 +1,61 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Map; - -/** - * A restricted implementation of {@link Map.Entry Map.Entry} that prevents - * the {@link Map.Entry Map.Entry} contract from being broken. - * - * @param the type of keys - * @param the type of mapped values - * @since 3.0 - */ -public final class DefaultMapEntry extends AbstractMapEntry { - - /** - * Constructs a new entry with the specified key and given value. - * - * @param key the key for the entry, may be null - * @param value the value for the entry, may be null - */ - public DefaultMapEntry(final K key, final V value) { - super(key, value); - } - - /** - * Constructs a new entry from the specified KeyValue. - * - * @param pair the pair to copy, must not be null - * @throws NullPointerException if the entry is null - */ - public DefaultMapEntry(final KeyValue pair) { - super(pair.getKey(), pair.getValue()); - } - - /** - * Constructs a new entry from the specified Map.Entry. - * - * @param entry the entry to copy, must not be null - * @throws NullPointerException if the entry is null - */ - public DefaultMapEntry(final Map.Entry entry) { - super(entry.getKey(), entry.getValue()); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Map; + +/** + * A restricted implementation of {@link Map.Entry Map.Entry} that prevents + * the {@link Map.Entry Map.Entry} contract from being broken. + * + * @param the type of keys + * @param the type of mapped values + * @since 3.0 + */ +public final class DefaultMapEntry extends AbstractMapEntry { + + /** + * Constructs a new entry with the specified key and given value. + * + * @param key the key for the entry, may be null + * @param value the value for the entry, may be null + */ + public DefaultMapEntry(final K key, final V value) { + super(key, value); + } + + /** + * Constructs a new entry from the specified KeyValue. + * + * @param pair the pair to copy, must not be null + * @throws NullPointerException if the entry is null + */ + public DefaultMapEntry(final KeyValue pair) { + super(pair.getKey(), pair.getValue()); + } + + /** + * Constructs a new entry from the specified Map.Entry. + * + * @param entry the entry to copy, must not be null + * @throws NullPointerException if the entry is null + */ + public DefaultMapEntry(final Map.Entry entry) { + super(entry.getKey(), entry.getValue()); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DualHashBidiMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DualHashBidiMap.java index 7a530dd1b1..d9504be9ce 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DualHashBidiMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DualHashBidiMap.java @@ -1,109 +1,109 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -/** - * Implementation of {@link BidiMap} that uses two {@link HashMap} instances. - *

- * Two {@link HashMap} instances are used in this class. - * This provides fast lookups at the expense of storing two sets of map entries. - * Commons Collections would welcome the addition of a direct hash-based - * implementation of the {@link BidiMap} interface. - *

- *

- * NOTE: From Commons Collections 3.1, all subclasses will use {@link HashMap} - * and the flawed createMap method is ignored. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * - * @since 3.0 - */ -public class DualHashBidiMap extends AbstractDualBidiMap implements Serializable { - - /** Ensure serialization compatibility */ - private static final long serialVersionUID = 721969328361808L; - - /** - * Creates an empty HashBidiMap. - */ - public DualHashBidiMap() { - super(new HashMap<>(), new HashMap<>()); - } - - /** - * Constructs a HashBidiMap and copies the mappings from - * specified Map. - * - * @param map the map whose mappings are to be placed in this map - */ - public DualHashBidiMap(final Map map) { - super(new HashMap<>(), new HashMap<>()); - putAll(map); - } - - /** - * Constructs a HashBidiMap that decorates the specified maps. - * - * @param normalMap the normal direction map - * @param reverseMap the reverse direction map - * @param inverseBidiMap the inverse BidiMap - */ - protected DualHashBidiMap(final Map normalMap, final Map reverseMap, - final BidiMap inverseBidiMap) { - super(normalMap, reverseMap, inverseBidiMap); - } - - /** - * Creates a new instance of this object. - * - * @param normalMap the normal direction map - * @param reverseMap the reverse direction map - * @param inverseBidiMap the inverse BidiMap - * @return new bidi map - */ - @Override - protected BidiMap createBidiMap(final Map normalMap, final Map reverseMap, - final BidiMap inverseBidiMap) { - return new DualHashBidiMap<>(normalMap, reverseMap, inverseBidiMap); - } - - // Serialization - //----------------------------------------------------------------------- - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(normalMap); - } - - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - normalMap = new HashMap<>(); - reverseMap = new HashMap<>(); - @SuppressWarnings("unchecked") // will fail at runtime if stream is incorrect - final Map map = (Map) in.readObject(); - putAll(map); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Implementation of {@link BidiMap} that uses two {@link HashMap} instances. + *

+ * Two {@link HashMap} instances are used in this class. + * This provides fast lookups at the expense of storing two sets of map entries. + * Commons Collections would welcome the addition of a direct hash-based + * implementation of the {@link BidiMap} interface. + *

+ *

+ * NOTE: From Commons Collections 3.1, all subclasses will use {@link HashMap} + * and the flawed createMap method is ignored. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * + * @since 3.0 + */ +public class DualHashBidiMap extends AbstractDualBidiMap implements Serializable { + + /** Ensure serialization compatibility */ + private static final long serialVersionUID = 721969328361808L; + + /** + * Creates an empty HashBidiMap. + */ + public DualHashBidiMap() { + super(new HashMap<>(), new HashMap<>()); + } + + /** + * Constructs a HashBidiMap and copies the mappings from + * specified Map. + * + * @param map the map whose mappings are to be placed in this map + */ + public DualHashBidiMap(final Map map) { + super(new HashMap<>(), new HashMap<>()); + putAll(map); + } + + /** + * Constructs a HashBidiMap that decorates the specified maps. + * + * @param normalMap the normal direction map + * @param reverseMap the reverse direction map + * @param inverseBidiMap the inverse BidiMap + */ + protected DualHashBidiMap(final Map normalMap, final Map reverseMap, + final BidiMap inverseBidiMap) { + super(normalMap, reverseMap, inverseBidiMap); + } + + /** + * Creates a new instance of this object. + * + * @param normalMap the normal direction map + * @param reverseMap the reverse direction map + * @param inverseBidiMap the inverse BidiMap + * @return new bidi map + */ + @Override + protected BidiMap createBidiMap(final Map normalMap, final Map reverseMap, + final BidiMap inverseBidiMap) { + return new DualHashBidiMap<>(normalMap, reverseMap, inverseBidiMap); + } + + // Serialization + //----------------------------------------------------------------------- + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(normalMap); + } + + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + normalMap = new HashMap<>(); + reverseMap = new HashMap<>(); + @SuppressWarnings("unchecked") // will fail at runtime if stream is incorrect + final Map map = (Map) in.readObject(); + putAll(map); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DualTreeBidiMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DualTreeBidiMap.java index 0d46f77b95..196561441b 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DualTreeBidiMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/DualTreeBidiMap.java @@ -1,411 +1,411 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.ListIterator; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -/** - * Implementation of {@link BidiMap} that uses two {@link TreeMap} instances. - *

- * The setValue() method on iterators will succeed only if the new value being set is - * not already in the bidimap. - *

- *

- * When considering whether to use this class, the {@link TreeBidiMap} class should - * also be considered. It implements the interface using a dedicated design, and does - * not store each object twice, which can save on memory use. - *

- *

- * NOTE: From Commons Collections 3.1, all subclasses will use {@link TreeMap} - * and the flawed createMap method is ignored. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * @since 3.0 - */ -public class DualTreeBidiMap extends AbstractDualBidiMap - implements SortedBidiMap, Serializable { - - /** Ensure serialization compatibility */ - private static final long serialVersionUID = 721969328361809L; - - /** The key comparator to use */ - private final Comparator comparator; - - /** The value comparator to use */ - private final Comparator valueComparator; - - /** - * Creates an empty DualTreeBidiMap - */ - public DualTreeBidiMap() { - super(new TreeMap<>(), new TreeMap<>()); - this.comparator = null; - this.valueComparator = null; - } - - /** - * Constructs a DualTreeBidiMap and copies the mappings from - * specified Map. - * - * @param map the map whose mappings are to be placed in this map - */ - public DualTreeBidiMap(final Map map) { - super(new TreeMap<>(), new TreeMap<>()); - putAll(map); - this.comparator = null; - this.valueComparator = null; - } - - /** - * Constructs a {@link DualTreeBidiMap} using the specified {@link Comparator}. - * - * @param keyComparator the comparator - * @param valueComparator the values comparator to use - */ - public DualTreeBidiMap(final Comparator keyComparator, final Comparator valueComparator) { - super(new TreeMap<>(keyComparator), new TreeMap<>(valueComparator)); - this.comparator = keyComparator; - this.valueComparator = valueComparator; - } - - /** - * Constructs a {@link DualTreeBidiMap} that decorates the specified maps. - * - * @param normalMap the normal direction map - * @param reverseMap the reverse direction map - * @param inverseBidiMap the inverse BidiMap - */ - protected DualTreeBidiMap(final Map normalMap, final Map reverseMap, - final BidiMap inverseBidiMap) { - super(normalMap, reverseMap, inverseBidiMap); - this.comparator = ((SortedMap) normalMap).comparator(); - this.valueComparator = ((SortedMap) reverseMap).comparator(); - } - - /** - * Creates a new instance of this object. - * - * @param normalMap the normal direction map - * @param reverseMap the reverse direction map - * @param inverseMap the inverse BidiMap - * @return new bidi map - */ - @Override - protected DualTreeBidiMap createBidiMap(final Map normalMap, final Map reverseMap, - final BidiMap inverseMap) { - return new DualTreeBidiMap<>(normalMap, reverseMap, inverseMap); - } - - //----------------------------------------------------------------------- - - @Override - public Comparator comparator() { - return ((SortedMap) normalMap).comparator(); - } - - @Override - public Comparator valueComparator() { - return ((SortedMap) reverseMap).comparator(); - } - - @Override - public K firstKey() { - return ((SortedMap) normalMap).firstKey(); - } - - @Override - public K lastKey() { - return ((SortedMap) normalMap).lastKey(); - } - - @Override - public K nextKey(final K key) { - if (isEmpty()) { - return null; - } - if (normalMap instanceof OrderedMap) { - return ((OrderedMap) normalMap).nextKey(key); - } - final SortedMap sm = (SortedMap) normalMap; - final Iterator it = sm.tailMap(key).keySet().iterator(); - it.next(); - if (it.hasNext()) { - return it.next(); - } - return null; - } - - @Override - public K previousKey(final K key) { - if (isEmpty()) { - return null; - } - if (normalMap instanceof OrderedMap) { - return ((OrderedMap) normalMap).previousKey(key); - } - final SortedMap sm = (SortedMap) normalMap; - final SortedMap hm = sm.headMap(key); - if (hm.isEmpty()) { - return null; - } - return hm.lastKey(); - } - - //----------------------------------------------------------------------- - /** - * Obtains an ordered map iterator. - *

- * This implementation copies the elements to an ArrayList in order to - * provide the forward/backward behaviour. - * - * @return a new ordered map iterator - */ - @Override - public OrderedMapIterator mapIterator() { - return new BidiOrderedMapIterator<>(this); - } - - public SortedBidiMap inverseSortedBidiMap() { - return inverseBidiMap(); - } - - public OrderedBidiMap inverseOrderedBidiMap() { - return inverseBidiMap(); - } - - //----------------------------------------------------------------------- - - @Override - public SortedMap headMap(final K toKey) { - final SortedMap sub = ((SortedMap) normalMap).headMap(toKey); - return new ViewMap<>(this, sub); - } - - @Override - public SortedMap tailMap(final K fromKey) { - final SortedMap sub = ((SortedMap) normalMap).tailMap(fromKey); - return new ViewMap<>(this, sub); - } - - @Override - public SortedMap subMap(final K fromKey, final K toKey) { - final SortedMap sub = ((SortedMap) normalMap).subMap(fromKey, toKey); - return new ViewMap<>(this, sub); - } - - @Override - public SortedBidiMap inverseBidiMap() { - return (SortedBidiMap) super.inverseBidiMap(); - } - - //----------------------------------------------------------------------- - /** - * Internal sorted map view. - */ - protected static class ViewMap extends AbstractSortedMapDecorator { - /** - * Constructor. - * @param bidi the parent bidi map - * @param sm the subMap sorted map - */ - protected ViewMap(final DualTreeBidiMap bidi, final SortedMap sm) { - // the implementation is not great here... - // use the normalMap as the filtered map, but reverseMap as the full map - // this forces containsValue and clear to be overridden - super(new DualTreeBidiMap<>(sm, bidi.reverseMap, bidi.inverseBidiMap)); - } - - @Override - public boolean containsValue(final Object value) { - // override as default implementation uses reverseMap - return decorated().normalMap.containsValue(value); - } - - @Override - public void clear() { - // override as default implementation uses reverseMap - for (final Iterator it = keySet().iterator(); it.hasNext();) { - it.next(); - it.remove(); - } - } - - @Override - public SortedMap headMap(final K toKey) { - return new ViewMap<>(decorated(), super.headMap(toKey)); - } - - @Override - public SortedMap tailMap(final K fromKey) { - return new ViewMap<>(decorated(), super.tailMap(fromKey)); - } - - @Override - public SortedMap subMap(final K fromKey, final K toKey) { - return new ViewMap<>(decorated(), super.subMap(fromKey, toKey)); - } - - @Override - protected DualTreeBidiMap decorated() { - return (DualTreeBidiMap) super.decorated(); - } - - @Override - public K previousKey(final K key) { - return decorated().previousKey(key); - } - - @Override - public K nextKey(final K key) { - return decorated().nextKey(key); - } - } - - //----------------------------------------------------------------------- - /** - * Inner class MapIterator. - */ - protected static class BidiOrderedMapIterator implements OrderedMapIterator, ResettableIterator { - - /** The parent map */ - private final AbstractDualBidiMap parent; - - /** The iterator being decorated */ - private ListIterator> iterator; - - /** The last returned entry */ - private Entry last = null; - - /** - * Constructor. - * @param parent the parent map - */ - protected BidiOrderedMapIterator(final AbstractDualBidiMap parent) { - super(); - this.parent = parent; - iterator = new ArrayList<>(parent.entrySet()).listIterator(); - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public K next() { - last = iterator.next(); - return last.getKey(); - } - - @Override - public boolean hasPrevious() { - return iterator.hasPrevious(); - } - - @Override - public K previous() { - last = iterator.previous(); - return last.getKey(); - } - - @Override - public void remove() { - iterator.remove(); - parent.remove(last.getKey()); - last = null; - } - - @Override - public K getKey() { - if (last == null) { - throw new IllegalStateException( - "Iterator getKey() can only be called after next() and before remove()"); - } - return last.getKey(); - } - - @Override - public V getValue() { - if (last == null) { - throw new IllegalStateException( - "Iterator getValue() can only be called after next() and before remove()"); - } - return last.getValue(); - } - - @Override - public V setValue(final V value) { - if (last == null) { - throw new IllegalStateException( - "Iterator setValue() can only be called after next() and before remove()"); - } - if (parent.reverseMap.containsKey(value) && - parent.reverseMap.get(value) != last.getKey()) { - throw new IllegalArgumentException( - "Cannot use setValue() when the object being set is already in the map"); - } - final V oldValue = parent.put(last.getKey(), value); - // Map.Entry specifies that the behavior is undefined when the backing map - // has been modified (as we did with the put), so we also set the value - last.setValue(value); - return oldValue; - } - - @Override - public void reset() { - iterator = new ArrayList<>(parent.entrySet()).listIterator(); - last = null; - } - - @Override - public String toString() { - if (last != null) { - return "MapIterator[" + getKey() + "=" + getValue() + "]"; - } - return "MapIterator[]"; - } - } - - // Serialization - //----------------------------------------------------------------------- - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(normalMap); - } - - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - normalMap = new TreeMap<>(comparator); - reverseMap = new TreeMap<>(valueComparator); - @SuppressWarnings("unchecked") // will fail at runtime if the stream is incorrect - final Map map = (Map) in.readObject(); - putAll(map); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.ListIterator; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * Implementation of {@link BidiMap} that uses two {@link TreeMap} instances. + *

+ * The setValue() method on iterators will succeed only if the new value being set is + * not already in the bidimap. + *

+ *

+ * When considering whether to use this class, the {@link TreeBidiMap} class should + * also be considered. It implements the interface using a dedicated design, and does + * not store each object twice, which can save on memory use. + *

+ *

+ * NOTE: From Commons Collections 3.1, all subclasses will use {@link TreeMap} + * and the flawed createMap method is ignored. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * @since 3.0 + */ +public class DualTreeBidiMap extends AbstractDualBidiMap + implements SortedBidiMap, Serializable { + + /** Ensure serialization compatibility */ + private static final long serialVersionUID = 721969328361809L; + + /** The key comparator to use */ + private final Comparator comparator; + + /** The value comparator to use */ + private final Comparator valueComparator; + + /** + * Creates an empty DualTreeBidiMap + */ + public DualTreeBidiMap() { + super(new TreeMap<>(), new TreeMap<>()); + this.comparator = null; + this.valueComparator = null; + } + + /** + * Constructs a DualTreeBidiMap and copies the mappings from + * specified Map. + * + * @param map the map whose mappings are to be placed in this map + */ + public DualTreeBidiMap(final Map map) { + super(new TreeMap<>(), new TreeMap<>()); + putAll(map); + this.comparator = null; + this.valueComparator = null; + } + + /** + * Constructs a {@link DualTreeBidiMap} using the specified {@link Comparator}. + * + * @param keyComparator the comparator + * @param valueComparator the values comparator to use + */ + public DualTreeBidiMap(final Comparator keyComparator, final Comparator valueComparator) { + super(new TreeMap<>(keyComparator), new TreeMap<>(valueComparator)); + this.comparator = keyComparator; + this.valueComparator = valueComparator; + } + + /** + * Constructs a {@link DualTreeBidiMap} that decorates the specified maps. + * + * @param normalMap the normal direction map + * @param reverseMap the reverse direction map + * @param inverseBidiMap the inverse BidiMap + */ + protected DualTreeBidiMap(final Map normalMap, final Map reverseMap, + final BidiMap inverseBidiMap) { + super(normalMap, reverseMap, inverseBidiMap); + this.comparator = ((SortedMap) normalMap).comparator(); + this.valueComparator = ((SortedMap) reverseMap).comparator(); + } + + /** + * Creates a new instance of this object. + * + * @param normalMap the normal direction map + * @param reverseMap the reverse direction map + * @param inverseMap the inverse BidiMap + * @return new bidi map + */ + @Override + protected DualTreeBidiMap createBidiMap(final Map normalMap, final Map reverseMap, + final BidiMap inverseMap) { + return new DualTreeBidiMap<>(normalMap, reverseMap, inverseMap); + } + + //----------------------------------------------------------------------- + + @Override + public Comparator comparator() { + return ((SortedMap) normalMap).comparator(); + } + + @Override + public Comparator valueComparator() { + return ((SortedMap) reverseMap).comparator(); + } + + @Override + public K firstKey() { + return ((SortedMap) normalMap).firstKey(); + } + + @Override + public K lastKey() { + return ((SortedMap) normalMap).lastKey(); + } + + @Override + public K nextKey(final K key) { + if (isEmpty()) { + return null; + } + if (normalMap instanceof OrderedMap) { + return ((OrderedMap) normalMap).nextKey(key); + } + final SortedMap sm = (SortedMap) normalMap; + final Iterator it = sm.tailMap(key).keySet().iterator(); + it.next(); + if (it.hasNext()) { + return it.next(); + } + return null; + } + + @Override + public K previousKey(final K key) { + if (isEmpty()) { + return null; + } + if (normalMap instanceof OrderedMap) { + return ((OrderedMap) normalMap).previousKey(key); + } + final SortedMap sm = (SortedMap) normalMap; + final SortedMap hm = sm.headMap(key); + if (hm.isEmpty()) { + return null; + } + return hm.lastKey(); + } + + //----------------------------------------------------------------------- + /** + * Obtains an ordered map iterator. + *

+ * This implementation copies the elements to an ArrayList in order to + * provide the forward/backward behaviour. + * + * @return a new ordered map iterator + */ + @Override + public OrderedMapIterator mapIterator() { + return new BidiOrderedMapIterator<>(this); + } + + public SortedBidiMap inverseSortedBidiMap() { + return inverseBidiMap(); + } + + public OrderedBidiMap inverseOrderedBidiMap() { + return inverseBidiMap(); + } + + //----------------------------------------------------------------------- + + @Override + public SortedMap headMap(final K toKey) { + final SortedMap sub = ((SortedMap) normalMap).headMap(toKey); + return new ViewMap<>(this, sub); + } + + @Override + public SortedMap tailMap(final K fromKey) { + final SortedMap sub = ((SortedMap) normalMap).tailMap(fromKey); + return new ViewMap<>(this, sub); + } + + @Override + public SortedMap subMap(final K fromKey, final K toKey) { + final SortedMap sub = ((SortedMap) normalMap).subMap(fromKey, toKey); + return new ViewMap<>(this, sub); + } + + @Override + public SortedBidiMap inverseBidiMap() { + return (SortedBidiMap) super.inverseBidiMap(); + } + + //----------------------------------------------------------------------- + /** + * Internal sorted map view. + */ + protected static class ViewMap extends AbstractSortedMapDecorator { + /** + * Constructor. + * @param bidi the parent bidi map + * @param sm the subMap sorted map + */ + protected ViewMap(final DualTreeBidiMap bidi, final SortedMap sm) { + // the implementation is not great here... + // use the normalMap as the filtered map, but reverseMap as the full map + // this forces containsValue and clear to be overridden + super(new DualTreeBidiMap<>(sm, bidi.reverseMap, bidi.inverseBidiMap)); + } + + @Override + public boolean containsValue(final Object value) { + // override as default implementation uses reverseMap + return decorated().normalMap.containsValue(value); + } + + @Override + public void clear() { + // override as default implementation uses reverseMap + for (final Iterator it = keySet().iterator(); it.hasNext();) { + it.next(); + it.remove(); + } + } + + @Override + public SortedMap headMap(final K toKey) { + return new ViewMap<>(decorated(), super.headMap(toKey)); + } + + @Override + public SortedMap tailMap(final K fromKey) { + return new ViewMap<>(decorated(), super.tailMap(fromKey)); + } + + @Override + public SortedMap subMap(final K fromKey, final K toKey) { + return new ViewMap<>(decorated(), super.subMap(fromKey, toKey)); + } + + @Override + protected DualTreeBidiMap decorated() { + return (DualTreeBidiMap) super.decorated(); + } + + @Override + public K previousKey(final K key) { + return decorated().previousKey(key); + } + + @Override + public K nextKey(final K key) { + return decorated().nextKey(key); + } + } + + //----------------------------------------------------------------------- + /** + * Inner class MapIterator. + */ + protected static class BidiOrderedMapIterator implements OrderedMapIterator, ResettableIterator { + + /** The parent map */ + private final AbstractDualBidiMap parent; + + /** The iterator being decorated */ + private ListIterator> iterator; + + /** The last returned entry */ + private Entry last = null; + + /** + * Constructor. + * @param parent the parent map + */ + protected BidiOrderedMapIterator(final AbstractDualBidiMap parent) { + super(); + this.parent = parent; + iterator = new ArrayList<>(parent.entrySet()).listIterator(); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public K next() { + last = iterator.next(); + return last.getKey(); + } + + @Override + public boolean hasPrevious() { + return iterator.hasPrevious(); + } + + @Override + public K previous() { + last = iterator.previous(); + return last.getKey(); + } + + @Override + public void remove() { + iterator.remove(); + parent.remove(last.getKey()); + last = null; + } + + @Override + public K getKey() { + if (last == null) { + throw new IllegalStateException( + "Iterator getKey() can only be called after next() and before remove()"); + } + return last.getKey(); + } + + @Override + public V getValue() { + if (last == null) { + throw new IllegalStateException( + "Iterator getValue() can only be called after next() and before remove()"); + } + return last.getValue(); + } + + @Override + public V setValue(final V value) { + if (last == null) { + throw new IllegalStateException( + "Iterator setValue() can only be called after next() and before remove()"); + } + if (parent.reverseMap.containsKey(value) && + parent.reverseMap.get(value) != last.getKey()) { + throw new IllegalArgumentException( + "Cannot use setValue() when the object being set is already in the map"); + } + final V oldValue = parent.put(last.getKey(), value); + // Map.Entry specifies that the behavior is undefined when the backing map + // has been modified (as we did with the put), so we also set the value + last.setValue(value); + return oldValue; + } + + @Override + public void reset() { + iterator = new ArrayList<>(parent.entrySet()).listIterator(); + last = null; + } + + @Override + public String toString() { + if (last != null) { + return "MapIterator[" + getKey() + "=" + getValue() + "]"; + } + return "MapIterator[]"; + } + } + + // Serialization + //----------------------------------------------------------------------- + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(normalMap); + } + + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + normalMap = new TreeMap<>(comparator); + reverseMap = new TreeMap<>(valueComparator); + @SuppressWarnings("unchecked") // will fail at runtime if the stream is incorrect + final Map map = (Map) in.readObject(); + putAll(map); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyIterator.java index ef8c209ab7..1d05751ff6 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyIterator.java @@ -1,71 +1,71 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Iterator; - -/** - * Provides an implementation of an empty iterator. - *

- * This class provides an implementation of an empty iterator. - * This class provides for binary compatibility between Commons Collections - * 2.1.1 and 3.1 due to issues with IteratorUtils. - * - * @since 2.1.1 and 3.1 - */ -public class EmptyIterator extends AbstractEmptyIterator implements ResettableIterator { - - /** - * Singleton instance of the iterator. - * @since 3.1 - */ - @SuppressWarnings("rawtypes") - public static final ResettableIterator RESETTABLE_INSTANCE = new EmptyIterator<>(); - - /** - * Singleton instance of the iterator. - * @since 2.1.1 and 3.1 - */ - @SuppressWarnings("rawtypes") - public static final Iterator INSTANCE = RESETTABLE_INSTANCE; - - /** - * Get a typed resettable empty iterator instance. - * @param the element type - * @return ResettableIterator<E> - */ - public static ResettableIterator resettableEmptyIterator() { - return RESETTABLE_INSTANCE; - } - - /** - * Get a typed empty iterator instance. - * @param the element type - * @return Iterator<E> - */ - public static Iterator emptyIterator() { - return INSTANCE; - } - - /** - * Constructor. - */ - protected EmptyIterator() { - super(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Iterator; + +/** + * Provides an implementation of an empty iterator. + *

+ * This class provides an implementation of an empty iterator. + * This class provides for binary compatibility between Commons Collections + * 2.1.1 and 3.1 due to issues with IteratorUtils. + * + * @since 2.1.1 and 3.1 + */ +public class EmptyIterator extends AbstractEmptyIterator implements ResettableIterator { + + /** + * Singleton instance of the iterator. + * @since 3.1 + */ + @SuppressWarnings("rawtypes") + public static final ResettableIterator RESETTABLE_INSTANCE = new EmptyIterator<>(); + + /** + * Singleton instance of the iterator. + * @since 2.1.1 and 3.1 + */ + @SuppressWarnings("rawtypes") + public static final Iterator INSTANCE = RESETTABLE_INSTANCE; + + /** + * Get a typed resettable empty iterator instance. + * @param the element type + * @return ResettableIterator<E> + */ + public static ResettableIterator resettableEmptyIterator() { + return RESETTABLE_INSTANCE; + } + + /** + * Get a typed empty iterator instance. + * @param the element type + * @return Iterator<E> + */ + public static Iterator emptyIterator() { + return INSTANCE; + } + + /** + * Constructor. + */ + protected EmptyIterator() { + super(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyMapIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyMapIterator.java index adbb1aa4f1..16282462b4 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyMapIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyMapIterator.java @@ -1,53 +1,53 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Provides an implementation of an empty map iterator. - * - * @param the type of keys - * @param the type of mapped values - * @since 3.1 - */ -public class EmptyMapIterator extends AbstractEmptyMapIterator implements - MapIterator, ResettableIterator { - - /** - * Singleton instance of the iterator. - * @since 3.1 - */ - @SuppressWarnings("rawtypes") - public static final MapIterator INSTANCE = new EmptyMapIterator<>(); - - /** - * Get a typed instance of the iterator. - * @param the key type - * @param the value type - * @return {@link MapIterator}<K, V> - */ - public static MapIterator emptyMapIterator() { - return INSTANCE; - } - - /** - * Constructor. - */ - protected EmptyMapIterator() { - super(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Provides an implementation of an empty map iterator. + * + * @param the type of keys + * @param the type of mapped values + * @since 3.1 + */ +public class EmptyMapIterator extends AbstractEmptyMapIterator implements + MapIterator, ResettableIterator { + + /** + * Singleton instance of the iterator. + * @since 3.1 + */ + @SuppressWarnings("rawtypes") + public static final MapIterator INSTANCE = new EmptyMapIterator<>(); + + /** + * Get a typed instance of the iterator. + * @param the key type + * @param the value type + * @return {@link MapIterator}<K, V> + */ + public static MapIterator emptyMapIterator() { + return INSTANCE; + } + + /** + * Constructor. + */ + protected EmptyMapIterator() { + super(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyOrderedIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyOrderedIterator.java index 5e6264a438..f5d0f98839 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyOrderedIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyOrderedIterator.java @@ -1,51 +1,51 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Provides an implementation of an empty ordered iterator. - * - * @param the type to iterate. - * @since 3.1 - */ -public class EmptyOrderedIterator extends AbstractEmptyIterator - implements OrderedIterator, ResettableIterator { - - /** - * Singleton instance of the iterator. - * @since 3.1 - */ - @SuppressWarnings("rawtypes") - public static final OrderedIterator INSTANCE = new EmptyOrderedIterator<>(); - - /** - * Typed instance of the iterator. - * @param the element type - * @return OrderedIterator<E> - */ - public static OrderedIterator emptyOrderedIterator() { - return INSTANCE; - } - - /** - * Constructor. - */ - protected EmptyOrderedIterator() { - super(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Provides an implementation of an empty ordered iterator. + * + * @param the type to iterate. + * @since 3.1 + */ +public class EmptyOrderedIterator extends AbstractEmptyIterator + implements OrderedIterator, ResettableIterator { + + /** + * Singleton instance of the iterator. + * @since 3.1 + */ + @SuppressWarnings("rawtypes") + public static final OrderedIterator INSTANCE = new EmptyOrderedIterator<>(); + + /** + * Typed instance of the iterator. + * @param the element type + * @return OrderedIterator<E> + */ + public static OrderedIterator emptyOrderedIterator() { + return INSTANCE; + } + + /** + * Constructor. + */ + protected EmptyOrderedIterator() { + super(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyOrderedMapIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyOrderedMapIterator.java index f85464d24d..4d97f84e9c 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyOrderedMapIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EmptyOrderedMapIterator.java @@ -1,53 +1,53 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Provides an implementation of an empty ordered map iterator. - * - * @param the type of keys - * @param the type of mapped values - * @since 3.1 - */ -public class EmptyOrderedMapIterator extends AbstractEmptyMapIterator - implements OrderedMapIterator, ResettableIterator { - - /** - * Singleton instance of the iterator. - * @since 3.1 - */ - @SuppressWarnings("rawtypes") - public static final OrderedMapIterator INSTANCE = new EmptyOrderedMapIterator<>(); - - /** - * Get a typed instance of the iterator. - * @param the key type - * @param the value type - * @return {@link OrderedMapIterator}<K, V> - */ - public static OrderedMapIterator emptyOrderedMapIterator() { - return INSTANCE; - } - - /** - * Constructor. - */ - protected EmptyOrderedMapIterator() { - super(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Provides an implementation of an empty ordered map iterator. + * + * @param the type of keys + * @param the type of mapped values + * @since 3.1 + */ +public class EmptyOrderedMapIterator extends AbstractEmptyMapIterator + implements OrderedMapIterator, ResettableIterator { + + /** + * Singleton instance of the iterator. + * @since 3.1 + */ + @SuppressWarnings("rawtypes") + public static final OrderedMapIterator INSTANCE = new EmptyOrderedMapIterator<>(); + + /** + * Get a typed instance of the iterator. + * @param the key type + * @param the value type + * @return {@link OrderedMapIterator}<K, V> + */ + public static OrderedMapIterator emptyOrderedMapIterator() { + return INSTANCE; + } + + /** + * Constructor. + */ + protected EmptyOrderedMapIterator() { + super(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EntrySetToMapIteratorAdapter.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EntrySetToMapIteratorAdapter.java index 310874e613..787460e5ae 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EntrySetToMapIteratorAdapter.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/EntrySetToMapIteratorAdapter.java @@ -1,119 +1,119 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -/** - * Adapts a Map entrySet to the MapIterator interface. - * - * @param the type of the keys in the map - * @param the type of the values in the map - * - * @since 4.0 - */ -public class EntrySetToMapIteratorAdapter implements MapIterator, ResettableIterator { - - /** The adapted Map entry Set. */ - Set> entrySet; - - /** The resettable iterator in use. */ - transient Iterator> iterator; - - /** The currently positioned Map entry. */ - transient Map.Entry entry; - - /** - * Create a new EntrySetToMapIteratorAdapter. - * @param entrySet the entrySet to adapt - */ - public EntrySetToMapIteratorAdapter(final Set> entrySet) { - this.entrySet = entrySet; - reset(); - } - - /** - * {@inheritDoc} - */ - @Override - public K getKey() { - return current().getKey(); - } - - /** - * {@inheritDoc} - */ - @Override - public V getValue() { - return current().getValue(); - } - - /** - * {@inheritDoc} - */ - @Override - public V setValue(final V value) { - return current().setValue(value); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - /** - * {@inheritDoc} - */ - @Override - public K next() { - entry = iterator.next(); - return getKey(); - } - - /** - * {@inheritDoc} - */ - @Override - public synchronized void reset() { - iterator = entrySet.iterator(); - } - - /** - * {@inheritDoc} - */ - @Override - public void remove() { - iterator.remove(); - entry = null; - } - - /** - * Get the currently active entry. - * @return Map.Entry<K, V> - */ - protected synchronized Map.Entry current() { - if (entry == null) { - throw new IllegalStateException(); - } - return entry; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Adapts a Map entrySet to the MapIterator interface. + * + * @param the type of the keys in the map + * @param the type of the values in the map + * + * @since 4.0 + */ +public class EntrySetToMapIteratorAdapter implements MapIterator, ResettableIterator { + + /** The adapted Map entry Set. */ + Set> entrySet; + + /** The resettable iterator in use. */ + transient Iterator> iterator; + + /** The currently positioned Map entry. */ + transient Map.Entry entry; + + /** + * Create a new EntrySetToMapIteratorAdapter. + * @param entrySet the entrySet to adapt + */ + public EntrySetToMapIteratorAdapter(final Set> entrySet) { + this.entrySet = entrySet; + reset(); + } + + /** + * {@inheritDoc} + */ + @Override + public K getKey() { + return current().getKey(); + } + + /** + * {@inheritDoc} + */ + @Override + public V getValue() { + return current().getValue(); + } + + /** + * {@inheritDoc} + */ + @Override + public V setValue(final V value) { + return current().setValue(value); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + /** + * {@inheritDoc} + */ + @Override + public K next() { + entry = iterator.next(); + return getKey(); + } + + /** + * {@inheritDoc} + */ + @Override + public synchronized void reset() { + iterator = entrySet.iterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public void remove() { + iterator.remove(); + entry = null; + } + + /** + * Get the currently active entry. + * @return Map.Entry<K, V> + */ + protected synchronized Map.Entry current() { + if (entry == null) { + throw new IllegalStateException(); + } + return entry; + } +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/FilterIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/FilterIterator.java index a112362747..8342870634 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/FilterIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/FilterIterator.java @@ -1,184 +1,184 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.function.Predicate; - -/** - * Decorates another {@link Iterator} using a predicate to filter elements. - *

- * This iterator decorates the underlying iterator, only allowing through - * those elements that match the specified {@link Predicate Predicate}. - * - * @since 1.0 - */ -public class FilterIterator implements Iterator { - - /** The iterator being used */ - private Iterator iterator; - /** The predicate being used */ - private Predicate predicate; - /** The next object in the iteration */ - private E nextObject; - /** Whether the next object has been calculated yet */ - private boolean nextObjectSet = false; - - //----------------------------------------------------------------------- - /** - * Constructs a new FilterIterator that will not function - * until {@link #setIterator(Iterator) setIterator} is invoked. - */ - public FilterIterator() { - super(); - } - - /** - * Constructs a new FilterIterator that will not function - * until {@link #setPredicate(Predicate) setPredicate} is invoked. - * - * @param iterator the iterator to use - */ - public FilterIterator(final Iterator iterator) { - super(); - this.iterator = iterator; - } - - /** - * Constructs a new FilterIterator that will use the - * given iterator and predicate. - * - * @param iterator the iterator to use - * @param predicate the predicate to use - */ - public FilterIterator(final Iterator iterator, final Predicate predicate) { - super(); - this.iterator = iterator; - this.predicate = predicate; - } - - //----------------------------------------------------------------------- - /** - * Returns true if the underlying iterator contains an object that - * matches the predicate. - * - * @return true if there is another object that matches the predicate - * @throws NullPointerException if either the iterator or predicate are null - */ - @Override - public boolean hasNext() { - return nextObjectSet || setNextObject(); - } - - /** - * Returns the next object that matches the predicate. - * - * @return the next object which matches the given predicate - * @throws NullPointerException if either the iterator or predicate are null - * @throws NoSuchElementException if there are no more elements that - * match the predicate - */ - @Override - public E next() { - if (!nextObjectSet && !setNextObject()) { - throw new NoSuchElementException(); - } - nextObjectSet = false; - return nextObject; - } - - /** - * Removes from the underlying collection of the base iterator the last - * element returned by this iterator. - * This method can only be called - * if next() was called, but not after - * hasNext(), because the hasNext() call - * changes the base iterator. - * - * @throws IllegalStateException if hasNext() has already - * been called. - */ - @Override - public void remove() { - if (nextObjectSet) { - throw new IllegalStateException("remove() cannot be called"); - } - iterator.remove(); - } - - //----------------------------------------------------------------------- - /** - * Gets the iterator this iterator is using. - * - * @return the iterator - */ - public Iterator getIterator() { - return iterator; - } - - /** - * Sets the iterator for this iterator to use. - * If iteration has started, this effectively resets the iterator. - * - * @param iterator the iterator to use - */ - public void setIterator(final Iterator iterator) { - this.iterator = iterator; - nextObject = null; - nextObjectSet = false; - } - - //----------------------------------------------------------------------- - /** - * Gets the predicate this iterator is using. - * - * @return the predicate - */ - public Predicate getPredicate() { - return predicate; - } - - /** - * Sets the predicate this the iterator to use. - * - * @param predicate the predicate to use - */ - public void setPredicate(final Predicate predicate) { - this.predicate = predicate; - nextObject = null; - nextObjectSet = false; - } - - //----------------------------------------------------------------------- - /** - * Set nextObject to the next object. If there are no more - * objects then return false. Otherwise, return true. - */ - private boolean setNextObject() { - while (iterator.hasNext()) { - final E object = iterator.next(); - if (predicate.test(object)) { - nextObject = object; - nextObjectSet = true; - return true; - } - } - return false; - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.function.Predicate; + +/** + * Decorates another {@link Iterator} using a predicate to filter elements. + *

+ * This iterator decorates the underlying iterator, only allowing through + * those elements that match the specified {@link Predicate Predicate}. + * + * @since 1.0 + */ +public class FilterIterator implements Iterator { + + /** The iterator being used */ + private Iterator iterator; + /** The predicate being used */ + private Predicate predicate; + /** The next object in the iteration */ + private E nextObject; + /** Whether the next object has been calculated yet */ + private boolean nextObjectSet = false; + + //----------------------------------------------------------------------- + /** + * Constructs a new FilterIterator that will not function + * until {@link #setIterator(Iterator) setIterator} is invoked. + */ + public FilterIterator() { + super(); + } + + /** + * Constructs a new FilterIterator that will not function + * until {@link #setPredicate(Predicate) setPredicate} is invoked. + * + * @param iterator the iterator to use + */ + public FilterIterator(final Iterator iterator) { + super(); + this.iterator = iterator; + } + + /** + * Constructs a new FilterIterator that will use the + * given iterator and predicate. + * + * @param iterator the iterator to use + * @param predicate the predicate to use + */ + public FilterIterator(final Iterator iterator, final Predicate predicate) { + super(); + this.iterator = iterator; + this.predicate = predicate; + } + + //----------------------------------------------------------------------- + /** + * Returns true if the underlying iterator contains an object that + * matches the predicate. + * + * @return true if there is another object that matches the predicate + * @throws NullPointerException if either the iterator or predicate are null + */ + @Override + public boolean hasNext() { + return nextObjectSet || setNextObject(); + } + + /** + * Returns the next object that matches the predicate. + * + * @return the next object which matches the given predicate + * @throws NullPointerException if either the iterator or predicate are null + * @throws NoSuchElementException if there are no more elements that + * match the predicate + */ + @Override + public E next() { + if (!nextObjectSet && !setNextObject()) { + throw new NoSuchElementException(); + } + nextObjectSet = false; + return nextObject; + } + + /** + * Removes from the underlying collection of the base iterator the last + * element returned by this iterator. + * This method can only be called + * if next() was called, but not after + * hasNext(), because the hasNext() call + * changes the base iterator. + * + * @throws IllegalStateException if hasNext() has already + * been called. + */ + @Override + public void remove() { + if (nextObjectSet) { + throw new IllegalStateException("remove() cannot be called"); + } + iterator.remove(); + } + + //----------------------------------------------------------------------- + /** + * Gets the iterator this iterator is using. + * + * @return the iterator + */ + public Iterator getIterator() { + return iterator; + } + + /** + * Sets the iterator for this iterator to use. + * If iteration has started, this effectively resets the iterator. + * + * @param iterator the iterator to use + */ + public void setIterator(final Iterator iterator) { + this.iterator = iterator; + nextObject = null; + nextObjectSet = false; + } + + //----------------------------------------------------------------------- + /** + * Gets the predicate this iterator is using. + * + * @return the predicate + */ + public Predicate getPredicate() { + return predicate; + } + + /** + * Sets the predicate this the iterator to use. + * + * @param predicate the predicate to use + */ + public void setPredicate(final Predicate predicate) { + this.predicate = predicate; + nextObject = null; + nextObjectSet = false; + } + + //----------------------------------------------------------------------- + /** + * Set nextObject to the next object. If there are no more + * objects then return false. Otherwise, return true. + */ + private boolean setNextObject() { + while (iterator.hasNext()) { + final E object = iterator.next(); + if (predicate.test(object)) { + nextObject = object; + nextObjectSet = true; + return true; + } + } + return false; + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IterableMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IterableMap.java index b40b019857..fe44351ef8 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IterableMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IterableMap.java @@ -1,87 +1,87 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Map; - -/** - * Defines a map that can be iterated directly without needing to create an entry set. - *

- * A map iterator is an efficient way of iterating over maps. - * There is no need to access the entry set or use Map Entry objects. - *

- *
- * IterableMap<String,Integer> map = new HashedMap<String,Integer>();
- * MapIterator<String,Integer> it = map.mapIterator();
- * while (it.hasNext()) {
- *   String key = it.next();
- *   Integer value = it.getValue();
- *   it.setValue(value + 1);
- * }
- * 
- * - * @param the type of the keys in this map - * @param the type of the values in this map - * - * @since 3.0 - */ -public interface IterableMap extends Map { - /** - * @see Map#clear() - */ - void clear(); - - /** - * Note that the return type is Object, rather than V as in the Map interface. - * See the class Javadoc for further info. - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - * @return the previous value associated with key, or - * null if there was no mapping for key. - * (A null return can also indicate that the map - * previously associated null with key, - * if the implementation supports null values.) - * @see Map#put(Object, Object) - */ - V put(K key, V value); - - /** - * @param t mappings to be stored in this map - * @see Map#putAll(Map) - */ - void putAll(Map t); - - /** - * Obtains a MapIterator over the map. - *

- * A map iterator is an efficient way of iterating over maps. - * There is no need to access the entry set or use Map Entry objects. - *

-     * IterableMap<String,Integer> map = new HashedMap<String,Integer>();
-     * MapIterator<String,Integer> it = map.mapIterator();
-     * while (it.hasNext()) {
-     *   String key = it.next();
-     *   Integer value = it.getValue();
-     *   it.setValue(value + 1);
-     * }
-     * 
- * - * @return a map iterator - */ - MapIterator mapIterator(); -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Map; + +/** + * Defines a map that can be iterated directly without needing to create an entry set. + *

+ * A map iterator is an efficient way of iterating over maps. + * There is no need to access the entry set or use Map Entry objects. + *

+ *
+ * IterableMap<String,Integer> map = new HashedMap<String,Integer>();
+ * MapIterator<String,Integer> it = map.mapIterator();
+ * while (it.hasNext()) {
+ *   String key = it.next();
+ *   Integer value = it.getValue();
+ *   it.setValue(value + 1);
+ * }
+ * 
+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * + * @since 3.0 + */ +public interface IterableMap extends Map { + /** + * @see Map#clear() + */ + void clear(); + + /** + * Note that the return type is Object, rather than V as in the Map interface. + * See the class Javadoc for further info. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with key, or + * null if there was no mapping for key. + * (A null return can also indicate that the map + * previously associated null with key, + * if the implementation supports null values.) + * @see Map#put(Object, Object) + */ + V put(K key, V value); + + /** + * @param t mappings to be stored in this map + * @see Map#putAll(Map) + */ + void putAll(Map t); + + /** + * Obtains a MapIterator over the map. + *

+ * A map iterator is an efficient way of iterating over maps. + * There is no need to access the entry set or use Map Entry objects. + *

+     * IterableMap<String,Integer> map = new HashedMap<String,Integer>();
+     * MapIterator<String,Integer> it = map.mapIterator();
+     * while (it.hasNext()) {
+     *   String key = it.next();
+     *   Integer value = it.getValue();
+     *   it.setValue(value + 1);
+     * }
+     * 
+ * + * @return a map iterator + */ + MapIterator mapIterator(); +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IterableSortedMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IterableSortedMap.java index e08ea83f46..6e6aa3c668 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IterableSortedMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IterableSortedMap.java @@ -1,30 +1,30 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.SortedMap; - -/** - * {@link SortedMap} + {@link OrderedMap}. - * - * @param the type of the keys in the map - * @param the type of the values in the map - * - * @since 4.0 - */ -public interface IterableSortedMap extends SortedMap, OrderedMap { -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.SortedMap; + +/** + * {@link SortedMap} + {@link OrderedMap}. + * + * @param the type of the keys in the map + * @param the type of the values in the map + * + * @since 4.0 + */ +public interface IterableSortedMap extends SortedMap, OrderedMap { +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IteratorChain.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IteratorChain.java index 78ebf2b535..00446e4186 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IteratorChain.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/IteratorChain.java @@ -1,280 +1,280 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.Queue; - -/** - * An IteratorChain is an Iterator that wraps a number of Iterators. - *

- * This class makes multiple iterators look like one to the caller. When any - * method from the Iterator interface is called, the IteratorChain will delegate - * to a single underlying Iterator. The IteratorChain will invoke the Iterators - * in sequence until all Iterators are exhausted. - *

- * Under many circumstances, linking Iterators together in this manner is more - * efficient (and convenient) than reading out the contents of each Iterator - * into a List and creating a new Iterator. - *

- * Calling a method that adds new Iterator after a method in the Iterator - * interface has been called will result in an UnsupportedOperationException. - *

- * NOTE: As from version 3.0, the IteratorChain may contain no iterators. In - * this case the class will function as an empty iterator. - *

- * NOTE: As from version 4.0, the IteratorChain stores the iterators in a queue - * and removes any reference to them as soon as they are not used anymore. Thus - * the methods {@code setIterator(Iterator)} and {@code getIterators()} have been - * removed and {@link #size()} will return the number of remaining iterators in - * the queue. - * - * @since 2.1 - */ -public class IteratorChain implements Iterator { - - /** The chain of iterators */ - private final Queue> iteratorChain = new LinkedList<>(); - - /** The current iterator */ - private Iterator currentIterator = null; - - /** - * The "last used" Iterator is the Iterator upon which next() or hasNext() - * was most recently called used for the remove() operation only - */ - private Iterator lastUsedIterator = null; - - /** - * ComparatorChain is "locked" after the first time compare(Object,Object) - * is called - */ - private boolean isLocked = false; - - //----------------------------------------------------------------------- - /** - * Construct an IteratorChain with no Iterators. - *

- * You will normally use {@link #addIterator(Iterator)} to add some - * iterators after using this constructor. - */ - public IteratorChain() { - super(); - } - - /** - * Construct an IteratorChain with a single Iterator. - *

- * This method takes one iterator. The newly constructed iterator will - * iterate through that iterator. Thus calling this constructor on its own - * will have no effect other than decorating the input iterator. - *

- * You will normally use {@link #addIterator(Iterator)} to add some more - * iterators after using this constructor. - * - * @param iterator the first child iterator in the IteratorChain, not null - * @throws NullPointerException if the iterator is null - */ - public IteratorChain(final Iterator iterator) { - super(); - addIterator(iterator); - } - - /** - * Constructs a new IteratorChain over the two given iterators. - *

- * This method takes two iterators. The newly constructed iterator will - * iterate through each one of the input iterators in turn. - * - * @param first the first child iterator in the IteratorChain, not null - * @param second the second child iterator in the IteratorChain, not null - * @throws NullPointerException if either iterator is null - */ - public IteratorChain(final Iterator first, final Iterator second) { - super(); - addIterator(first); - addIterator(second); - } - - /** - * Constructs a new IteratorChain over the array of iterators. - *

- * This method takes an array of iterators. The newly constructed iterator - * will iterate through each one of the input iterators in turn. - * - * @param iteratorChain the array of iterators, not null - * @throws NullPointerException if iterators array is or contains null - */ - public IteratorChain(final Iterator... iteratorChain) { - super(); - for (final Iterator element : iteratorChain) { - addIterator(element); - } - } - - /** - * Constructs a new IteratorChain over the collection of - * iterators. - *

- * This method takes a collection of iterators. The newly constructed - * iterator will iterate through each one of the input iterators in turn. - * - * @param iteratorChain the collection of iterators, not null - * @throws NullPointerException if iterators collection is or contains null - * @throws ClassCastException if iterators collection doesn't contain an - * iterator - */ - public IteratorChain(final Collection> iteratorChain) { - super(); - for (final Iterator iterator : iteratorChain) { - addIterator(iterator); - } - } - - //----------------------------------------------------------------------- - /** - * Add an Iterator to the end of the chain - * - * @param iterator Iterator to add - * @throws IllegalStateException if I've already started iterating - * @throws NullPointerException if the iterator is null - */ - public void addIterator(final Iterator iterator) { - checkLocked(); - if (iterator == null) { - throw new NullPointerException("Iterator must not be null"); - } - iteratorChain.add(iterator); - } - - /** - * Returns the remaining number of Iterators in the current IteratorChain. - * - * @return Iterator count - */ - public int size() { - return iteratorChain.size(); - } - - /** - * Determine if modifications can still be made to the IteratorChain. - * IteratorChains cannot be modified once they have executed a method from - * the Iterator interface. - * - * @return true if IteratorChain cannot be modified, false if it can - */ - public boolean isLocked() { - return isLocked; - } - - /** - * Checks whether the iterator chain is now locked and in use. - */ - private void checkLocked() { - if (isLocked) { - throw new UnsupportedOperationException( - "IteratorChain cannot be changed after the first use of a method from the Iterator interface"); - } - } - - /** - * Lock the chain so no more iterators can be added. This must be called - * from all Iterator interface methods. - */ - private void lockChain() { - if (!isLocked) { - isLocked = true; - } - } - - /** - * Updates the current iterator field to ensure that the current Iterator is - * not exhausted - */ - protected void updateCurrentIterator() { - if (currentIterator == null) { - if (iteratorChain.isEmpty()) { - currentIterator = EmptyIterator. emptyIterator(); - } else { - currentIterator = iteratorChain.remove(); - } - // set last used iterator here, in case the user calls remove - // before calling hasNext() or next() (although they shouldn't) - lastUsedIterator = currentIterator; - } - - while (!currentIterator.hasNext() && !iteratorChain.isEmpty()) { - currentIterator = iteratorChain.remove(); - } - } - - //----------------------------------------------------------------------- - /** - * Return true if any Iterator in the IteratorChain has a remaining element. - * - * @return true if elements remain - */ - @Override - public boolean hasNext() { - lockChain(); - updateCurrentIterator(); - lastUsedIterator = currentIterator; - - return currentIterator.hasNext(); - } - - /** - * Returns the next Object of the current Iterator - * - * @return Object from the current Iterator - * @throws java.util.NoSuchElementException if all the Iterators are - * exhausted - */ - @Override - public E next() { - lockChain(); - updateCurrentIterator(); - lastUsedIterator = currentIterator; - - return currentIterator.next(); - } - - /** - * Removes from the underlying collection the last element returned by the - * Iterator. As with next() and hasNext(), this method calls remove() on the - * underlying Iterator. Therefore, this method may throw an - * UnsupportedOperationException if the underlying Iterator does not support - * this method. - * - * @throws UnsupportedOperationException if the remove operator is not - * supported by the underlying Iterator - * @throws IllegalStateException if the next method has not yet been called, - * or the remove method has already been called after the last call to the - * next method. - */ - @Override - public void remove() { - lockChain(); - if (currentIterator == null) { - updateCurrentIterator(); - } - lastUsedIterator.remove(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Queue; + +/** + * An IteratorChain is an Iterator that wraps a number of Iterators. + *

+ * This class makes multiple iterators look like one to the caller. When any + * method from the Iterator interface is called, the IteratorChain will delegate + * to a single underlying Iterator. The IteratorChain will invoke the Iterators + * in sequence until all Iterators are exhausted. + *

+ * Under many circumstances, linking Iterators together in this manner is more + * efficient (and convenient) than reading out the contents of each Iterator + * into a List and creating a new Iterator. + *

+ * Calling a method that adds new Iterator after a method in the Iterator + * interface has been called will result in an UnsupportedOperationException. + *

+ * NOTE: As from version 3.0, the IteratorChain may contain no iterators. In + * this case the class will function as an empty iterator. + *

+ * NOTE: As from version 4.0, the IteratorChain stores the iterators in a queue + * and removes any reference to them as soon as they are not used anymore. Thus + * the methods {@code setIterator(Iterator)} and {@code getIterators()} have been + * removed and {@link #size()} will return the number of remaining iterators in + * the queue. + * + * @since 2.1 + */ +public class IteratorChain implements Iterator { + + /** The chain of iterators */ + private final Queue> iteratorChain = new LinkedList<>(); + + /** The current iterator */ + private Iterator currentIterator = null; + + /** + * The "last used" Iterator is the Iterator upon which next() or hasNext() + * was most recently called used for the remove() operation only + */ + private Iterator lastUsedIterator = null; + + /** + * ComparatorChain is "locked" after the first time compare(Object,Object) + * is called + */ + private boolean isLocked = false; + + //----------------------------------------------------------------------- + /** + * Construct an IteratorChain with no Iterators. + *

+ * You will normally use {@link #addIterator(Iterator)} to add some + * iterators after using this constructor. + */ + public IteratorChain() { + super(); + } + + /** + * Construct an IteratorChain with a single Iterator. + *

+ * This method takes one iterator. The newly constructed iterator will + * iterate through that iterator. Thus calling this constructor on its own + * will have no effect other than decorating the input iterator. + *

+ * You will normally use {@link #addIterator(Iterator)} to add some more + * iterators after using this constructor. + * + * @param iterator the first child iterator in the IteratorChain, not null + * @throws NullPointerException if the iterator is null + */ + public IteratorChain(final Iterator iterator) { + super(); + addIterator(iterator); + } + + /** + * Constructs a new IteratorChain over the two given iterators. + *

+ * This method takes two iterators. The newly constructed iterator will + * iterate through each one of the input iterators in turn. + * + * @param first the first child iterator in the IteratorChain, not null + * @param second the second child iterator in the IteratorChain, not null + * @throws NullPointerException if either iterator is null + */ + public IteratorChain(final Iterator first, final Iterator second) { + super(); + addIterator(first); + addIterator(second); + } + + /** + * Constructs a new IteratorChain over the array of iterators. + *

+ * This method takes an array of iterators. The newly constructed iterator + * will iterate through each one of the input iterators in turn. + * + * @param iteratorChain the array of iterators, not null + * @throws NullPointerException if iterators array is or contains null + */ + public IteratorChain(final Iterator... iteratorChain) { + super(); + for (final Iterator element : iteratorChain) { + addIterator(element); + } + } + + /** + * Constructs a new IteratorChain over the collection of + * iterators. + *

+ * This method takes a collection of iterators. The newly constructed + * iterator will iterate through each one of the input iterators in turn. + * + * @param iteratorChain the collection of iterators, not null + * @throws NullPointerException if iterators collection is or contains null + * @throws ClassCastException if iterators collection doesn't contain an + * iterator + */ + public IteratorChain(final Collection> iteratorChain) { + super(); + for (final Iterator iterator : iteratorChain) { + addIterator(iterator); + } + } + + //----------------------------------------------------------------------- + /** + * Add an Iterator to the end of the chain + * + * @param iterator Iterator to add + * @throws IllegalStateException if I've already started iterating + * @throws NullPointerException if the iterator is null + */ + public void addIterator(final Iterator iterator) { + checkLocked(); + if (iterator == null) { + throw new NullPointerException("Iterator must not be null"); + } + iteratorChain.add(iterator); + } + + /** + * Returns the remaining number of Iterators in the current IteratorChain. + * + * @return Iterator count + */ + public int size() { + return iteratorChain.size(); + } + + /** + * Determine if modifications can still be made to the IteratorChain. + * IteratorChains cannot be modified once they have executed a method from + * the Iterator interface. + * + * @return true if IteratorChain cannot be modified, false if it can + */ + public boolean isLocked() { + return isLocked; + } + + /** + * Checks whether the iterator chain is now locked and in use. + */ + private void checkLocked() { + if (isLocked) { + throw new UnsupportedOperationException( + "IteratorChain cannot be changed after the first use of a method from the Iterator interface"); + } + } + + /** + * Lock the chain so no more iterators can be added. This must be called + * from all Iterator interface methods. + */ + private void lockChain() { + if (!isLocked) { + isLocked = true; + } + } + + /** + * Updates the current iterator field to ensure that the current Iterator is + * not exhausted + */ + protected void updateCurrentIterator() { + if (currentIterator == null) { + if (iteratorChain.isEmpty()) { + currentIterator = EmptyIterator. emptyIterator(); + } else { + currentIterator = iteratorChain.remove(); + } + // set last used iterator here, in case the user calls remove + // before calling hasNext() or next() (although they shouldn't) + lastUsedIterator = currentIterator; + } + + while (!currentIterator.hasNext() && !iteratorChain.isEmpty()) { + currentIterator = iteratorChain.remove(); + } + } + + //----------------------------------------------------------------------- + /** + * Return true if any Iterator in the IteratorChain has a remaining element. + * + * @return true if elements remain + */ + @Override + public boolean hasNext() { + lockChain(); + updateCurrentIterator(); + lastUsedIterator = currentIterator; + + return currentIterator.hasNext(); + } + + /** + * Returns the next Object of the current Iterator + * + * @return Object from the current Iterator + * @throws java.util.NoSuchElementException if all the Iterators are + * exhausted + */ + @Override + public E next() { + lockChain(); + updateCurrentIterator(); + lastUsedIterator = currentIterator; + + return currentIterator.next(); + } + + /** + * Removes from the underlying collection the last element returned by the + * Iterator. As with next() and hasNext(), this method calls remove() on the + * underlying Iterator. Therefore, this method may throw an + * UnsupportedOperationException if the underlying Iterator does not support + * this method. + * + * @throws UnsupportedOperationException if the remove operator is not + * supported by the underlying Iterator + * @throws IllegalStateException if the next method has not yet been called, + * or the remove method has already been called after the last call to the + * next method. + */ + @Override + public void remove() { + lockChain(); + if (currentIterator == null) { + updateCurrentIterator(); + } + lastUsedIterator.remove(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/KeyValue.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/KeyValue.java index c5d7ed3d15..93932c6595 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/KeyValue.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/KeyValue.java @@ -1,47 +1,47 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Defines a simple key value pair. - *

- * A Map Entry has considerable additional semantics over and above a simple - * key-value pair. This interface defines the minimum key value, with just the - * two get methods. - *

- * - * @param the type of the key - * @param the type of the value - * @since 3.0 - */ -public interface KeyValue { - - /** - * Gets the key from the pair. - * - * @return the key - */ - K getKey(); - - /** - * Gets the value from the pair. - * - * @return the value - */ - V getValue(); - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Defines a simple key value pair. + *

+ * A Map Entry has considerable additional semantics over and above a simple + * key-value pair. This interface defines the minimum key value, with just the + * two get methods. + *

+ * + * @param the type of the key + * @param the type of the value + * @since 3.0 + */ +public interface KeyValue { + + /** + * Gets the key from the pair. + * + * @return the key + */ + K getKey(); + + /** + * Gets the value from the pair. + * + * @return the value + */ + V getValue(); + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/LRUMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/LRUMap.java index 46a81308f5..ba95921b20 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/LRUMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/LRUMap.java @@ -1,530 +1,530 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.Map; - -/** - * A Map implementation with a fixed maximum size which removes - * the least recently used entry if an entry is added when full. - *

- * The least recently used algorithm works on the get and put operations only. - * Iteration of any kind, including setting the value by iteration, does not - * change the order. Queries such as containsKey and containsValue or access - * via views also do not change the order. - *

- *

- * A somewhat subtle ramification of the least recently used - * algorithm is that calls to {@link #get(Object)} stand a very good chance - * of modifying the map's iteration order and thus invalidating any - * iterators currently in use. It is therefore suggested that iterations - * over an {@link LRUMap} instance access entry values only through a - * {@link MapIterator} or {@link #entrySet()} iterator. - *

- *

- * The map implements OrderedMap and entries may be queried using - * the bidirectional OrderedMapIterator. The order returned is - * least recently used to most recently used. Iterators from map views can - * also be cast to OrderedIterator if required. - *

- *

- * All the available iterators can be reset back to the start by casting to - * ResettableIterator and calling reset(). - *

- *

- * Note that LRUMap is not synchronized and is not thread-safe. - * If you wish to use this map from multiple threads concurrently, you must use - * appropriate synchronization. The simplest approach is to wrap this map - * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw - * NullPointerException's when accessed by concurrent threads. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * @since 3.0 (previously in main package v1.0) - */ -public class LRUMap - extends AbstractLinkedMap implements BoundedMap, Serializable, Cloneable { - - /** Serialisation version */ - private static final long serialVersionUID = -612114643488955218L; - /** Default maximum size */ - protected static final int DEFAULT_MAX_SIZE = 100; - - /** Maximum size */ - private transient int maxSize; - /** Scan behaviour */ - private boolean scanUntilRemovable; - - /** - * Constructs a new empty map with a maximum size of 100. - */ - public LRUMap() { - this(DEFAULT_MAX_SIZE, DEFAULT_LOAD_FACTOR, false); - } - - /** - * Constructs a new, empty map with the specified maximum size. - * - * @param maxSize the maximum size of the map - * @throws IllegalArgumentException if the maximum size is less than one - */ - public LRUMap(final int maxSize) { - this(maxSize, DEFAULT_LOAD_FACTOR); - } - - /** - * Constructs a new, empty map with the specified maximum size. - * - * @param maxSize the maximum size of the map - * @param initialSize the initial size of the map - * @throws IllegalArgumentException if the maximum size is less than one - * @throws IllegalArgumentException if the initial size is negative or larger than the maximum size - * @since 4.1 - */ - public LRUMap(final int maxSize, final int initialSize) { - this(maxSize, initialSize, DEFAULT_LOAD_FACTOR); - } - - /** - * Constructs a new, empty map with the specified maximum size. - * - * @param maxSize the maximum size of the map - * @param scanUntilRemovable scan until a removeable entry is found, default false - * @throws IllegalArgumentException if the maximum size is less than one - * @since 3.1 - */ - public LRUMap(final int maxSize, final boolean scanUntilRemovable) { - this(maxSize, DEFAULT_LOAD_FACTOR, scanUntilRemovable); - } - - /** - * Constructs a new, empty map with the specified max capacity and - * load factor. - * - * @param maxSize the maximum size of the map - * @param loadFactor the load factor - * @throws IllegalArgumentException if the maximum size is less than one - * @throws IllegalArgumentException if the load factor is less than zero - */ - public LRUMap(final int maxSize, final float loadFactor) { - this(maxSize, loadFactor, false); - } - - /** - * Constructs a new, empty map with the specified max / initial capacity and - * load factor. - * - * @param maxSize the maximum size of the map - * @param initialSize the initial size of the map - * @param loadFactor the load factor - * @throws IllegalArgumentException if the maximum size is less than one - * @throws IllegalArgumentException if the initial size is negative or larger than the maximum size - * @throws IllegalArgumentException if the load factor is less than zero - * @since 4.1 - */ - public LRUMap(final int maxSize, final int initialSize, final float loadFactor) { - this(maxSize, initialSize, loadFactor, false); - } - - /** - * Constructs a new, empty map with the specified max capacity and load factor. - * - * @param maxSize the maximum size of the map - * @param loadFactor the load factor - * @param scanUntilRemovable scan until a removeable entry is found, default false - * @throws IllegalArgumentException if the maximum size is less than one - * @throws IllegalArgumentException if the load factor is less than zero - * @since 3.1 - */ - public LRUMap(final int maxSize, final float loadFactor, final boolean scanUntilRemovable) { - this(maxSize, maxSize, loadFactor, scanUntilRemovable); - } - - /** - * Constructs a new, empty map with the specified max / initial capacity and load factor. - * - * @param maxSize the maximum size of the map - * @param initialSize the initial size of the map - * @param loadFactor the load factor - * @param scanUntilRemovable scan until a removeable entry is found, default false - * @throws IllegalArgumentException if the maximum size is less than one - * @throws IllegalArgumentException if the initial size is negative or larger than the maximum size - * @throws IllegalArgumentException if the load factor is less than zero - * @since 4.1 - */ - public LRUMap(final int maxSize, - final int initialSize, - final float loadFactor, - final boolean scanUntilRemovable) { - - super(initialSize, loadFactor); - if (maxSize < 1) { - throw new IllegalArgumentException("LRUMap max size must be greater than 0"); - } - if (initialSize > maxSize) { - throw new IllegalArgumentException("LRUMap initial size must not be greather than max size"); - } - this.maxSize = maxSize; - this.scanUntilRemovable = scanUntilRemovable; - } - - /** - * Constructor copying elements from another map. - *

- * The maximum size is set from the map's size. - * - * @param map the map to copy - * @throws NullPointerException if the map is null - * @throws IllegalArgumentException if the map is empty - */ - public LRUMap(final Map map) { - this(map, false); - } - - /** - * Constructor copying elements from another map. - * - *

The maximum size is set from the map's size.

- * - * @param map the map to copy - * @param scanUntilRemovable scan until a removeable entry is found, default false - * @throws NullPointerException if the map is null - * @throws IllegalArgumentException if the map is empty - * @since 3.1 - */ - public LRUMap(final Map map, final boolean scanUntilRemovable) { - this(map.size(), DEFAULT_LOAD_FACTOR, scanUntilRemovable); - putAll(map); - } - - //----------------------------------------------------------------------- - /** - * Gets the value mapped to the key specified. - *

- * This operation changes the position of the key in the map to the - * most recently used position (last). - * - * @param key the key - * @return the mapped value, null if no match - */ - @Override - public V get(final Object key) { - return get(key, true); - } - - /** - * Gets the value mapped to the key specified. - *

- * If {@code updateToMRU} is {@code true}, the position of the key in the map - * is changed to the most recently used position (last), otherwise the iteration - * order is not changed by this operation. - * - * @param key the key - * @param updateToMRU whether the key shall be updated to the - * most recently used position - * @return the mapped value, null if no match - * @since 4.1 - */ - public V get(final Object key, final boolean updateToMRU) { - final LinkEntry entry = getEntry(key); - if (entry == null) { - return null; - } - if (updateToMRU) { - moveToMRU(entry); - } - return entry.getValue(); - } - - //----------------------------------------------------------------------- - /** - * Moves an entry to the MRU position at the end of the list. - *

- * This implementation moves the updated entry to the end of the list. - * - * @param entry the entry to update - */ - protected void moveToMRU(final LinkEntry entry) { - if (entry.after != header) { - modCount++; - // remove - if(entry.before == null) { - throw new IllegalStateException("Entry.before is null." + - " This should not occur if your keys are immutable, and you have used synchronization properly."); - } - entry.before.after = entry.after; - entry.after.before = entry.before; - // add first - entry.after = header; - entry.before = header.before; - header.before.after = entry; - header.before = entry; - } else if (entry == header) { - throw new IllegalStateException("Can't move header to MRU" + - " This should not occur if your keys are immutable, and you have used synchronization properly."); - } - } - - /** - * Updates an existing key-value mapping. - *

- * This implementation moves the updated entry to the end of the list - * using {@link #moveToMRU(LinkEntry)}. - * - * @param entry the entry to update - * @param newValue the new value to store - */ - @Override - protected void updateEntry(final HashEntry entry, final V newValue) { - moveToMRU((LinkEntry) entry); // handles modCount - entry.setValue(newValue); - } - - /** - * Adds a new key-value mapping into this map. - *

- * This implementation checks the LRU size and determines whether to - * discard an entry or not using {@link #removeLRU(LinkEntry)}. - *

- * From Commons Collections 3.1 this method uses {@link #isFull()} rather - * than accessing size and maxSize directly. - * It also handles the scanUntilRemovable functionality. - * - * @param hashIndex the index into the data array to store at - * @param hashCode the hash code of the key to add - * @param key the key to add - * @param value the value to add - */ - @Override - protected void addMapping(final int hashIndex, final int hashCode, final K key, final V value) { - if (isFull()) { - LinkEntry reuse = header.after; - boolean removeLRUEntry = false; - if (scanUntilRemovable) { - while (reuse != header && reuse != null) { - if (removeLRU(reuse)) { - removeLRUEntry = true; - break; - } - reuse = reuse.after; - } - if (reuse == null) { - throw new IllegalStateException( - "Entry.after=null, header.after=" + header.after + " header.before=" + header.before + - " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + - " This should not occur if your keys are immutable, and you have used synchronization properly."); - } - } else { - removeLRUEntry = removeLRU(reuse); - } - - if (removeLRUEntry) { - if (reuse == null) { - throw new IllegalStateException( - "reuse=null, header.after=" + header.after + " header.before=" + header.before + - " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + - " This should not occur if your keys are immutable, and you have used synchronization properly."); - } - reuseMapping(reuse, hashIndex, hashCode, key, value); - } else { - super.addMapping(hashIndex, hashCode, key, value); - } - } else { - super.addMapping(hashIndex, hashCode, key, value); - } - } - - /** - * Reuses an entry by removing it and moving it to a new place in the map. - *

- * This method uses {@link #removeEntry}, {@link #reuseEntry} and {@link #addEntry}. - * - * @param entry the entry to reuse - * @param hashIndex the index into the data array to store at - * @param hashCode the hash code of the key to add - * @param key the key to add - * @param value the value to add - */ - protected void reuseMapping(final LinkEntry entry, final int hashIndex, final int hashCode, - final K key, final V value) { - // find the entry before the entry specified in the hash table - // remember that the parameters (except the first) refer to the new entry, - // not the old one - try { - final int removeIndex = hashIndex(entry.hashCode, data.length); - final HashEntry[] tmp = data; // may protect against some sync issues - HashEntry loop = tmp[removeIndex]; - HashEntry previous = null; - while (loop != entry && loop != null) { - previous = loop; - loop = loop.next; - } - if (loop == null) { - throw new IllegalStateException( - "Entry.next=null, data[removeIndex]=" + data[removeIndex] + " previous=" + previous + - " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + - " This should not occur if your keys are immutable, and you have used synchronization properly."); - } - - // reuse the entry - modCount++; - removeEntry(entry, removeIndex, previous); - reuseEntry(entry, hashIndex, hashCode, key, value); - addEntry(entry, hashIndex); - } catch (final NullPointerException ex) { - throw new IllegalStateException( - "NPE, entry=" + entry + " entryIsHeader=" + (entry==header) + - " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + - " This should not occur if your keys are immutable, and you have used synchronization properly."); - } - } - - /** - * Subclass method to control removal of the least recently used entry from the map. - *

- * This method exists for subclasses to override. A subclass may wish to - * provide cleanup of resources when an entry is removed. For example: - *

-     * protected boolean removeLRU(LinkEntry entry) {
-     *   releaseResources(entry.getValue());  // release resources held by entry
-     *   return true;  // actually delete entry
-     * }
-     * 
- *

- * Alternatively, a subclass may choose to not remove the entry or selectively - * keep certain LRU entries. For example: - *

-     * protected boolean removeLRU(LinkEntry entry) {
-     *   if (entry.getKey().toString().startsWith("System.")) {
-     *     return false;  // entry not removed from LRUMap
-     *   } else {
-     *     return true;  // actually delete entry
-     *   }
-     * }
-     * 
- * The effect of returning false is dependent on the scanUntilRemovable flag. - * If the flag is true, the next LRU entry will be passed to this method and so on - * until one returns false and is removed, or every entry in the map has been passed. - * If the scanUntilRemovable flag is false, the map will exceed the maximum size. - *

- * NOTE: Commons Collections 3.0 passed the wrong entry to this method. - * This is fixed in version 3.1 onwards. - * - * @param entry the entry to be removed - * @return {@code true} - */ - protected boolean removeLRU(final LinkEntry entry) { - return true; - } - - //----------------------------------------------------------------------- - /** - * Returns true if this map is full and no new mappings can be added. - * - * @return true if the map is full - */ - @Override - public boolean isFull() { - return size >= maxSize; - } - - /** - * Gets the maximum size of the map (the bound). - * - * @return the maximum number of elements the map can hold - */ - @Override - public int maxSize() { - return maxSize; - } - - /** - * Whether this LRUMap will scan until a removable entry is found when the - * map is full. - * - * @return true if this map scans - * @since 3.1 - */ - public boolean isScanUntilRemovable() { - return scanUntilRemovable; - } - - //----------------------------------------------------------------------- - /** - * Clones the map without cloning the keys or values. - * - * @return a shallow clone - */ - @Override - public LRUMap clone() { - return (LRUMap) super.clone(); - } - - /** - * Write the map out using a custom routine. - * - * @param out the output stream - * @throws IOException if an error occurs while writing to the stream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - doWriteObject(out); - } - - /** - * Read the map in using a custom routine. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - doReadObject(in); - } - - /** - * Writes the data necessary for put() to work in deserialization. - * - * @param out the output stream - * @throws IOException if an error occurs while writing to the stream - */ - @Override - protected void doWriteObject(final ObjectOutputStream out) throws IOException { - out.writeInt(maxSize); - super.doWriteObject(out); - } - - /** - * Reads the data necessary for put() to work in the superclass. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - @Override - protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - maxSize = in.readInt(); - super.doReadObject(in); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Map; + +/** + * A Map implementation with a fixed maximum size which removes + * the least recently used entry if an entry is added when full. + *

+ * The least recently used algorithm works on the get and put operations only. + * Iteration of any kind, including setting the value by iteration, does not + * change the order. Queries such as containsKey and containsValue or access + * via views also do not change the order. + *

+ *

+ * A somewhat subtle ramification of the least recently used + * algorithm is that calls to {@link #get(Object)} stand a very good chance + * of modifying the map's iteration order and thus invalidating any + * iterators currently in use. It is therefore suggested that iterations + * over an {@link LRUMap} instance access entry values only through a + * {@link MapIterator} or {@link #entrySet()} iterator. + *

+ *

+ * The map implements OrderedMap and entries may be queried using + * the bidirectional OrderedMapIterator. The order returned is + * least recently used to most recently used. Iterators from map views can + * also be cast to OrderedIterator if required. + *

+ *

+ * All the available iterators can be reset back to the start by casting to + * ResettableIterator and calling reset(). + *

+ *

+ * Note that LRUMap is not synchronized and is not thread-safe. + * If you wish to use this map from multiple threads concurrently, you must use + * appropriate synchronization. The simplest approach is to wrap this map + * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw + * NullPointerException's when accessed by concurrent threads. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * @since 3.0 (previously in main package v1.0) + */ +public class LRUMap + extends AbstractLinkedMap implements BoundedMap, Serializable, Cloneable { + + /** Serialisation version */ + private static final long serialVersionUID = -612114643488955218L; + /** Default maximum size */ + protected static final int DEFAULT_MAX_SIZE = 100; + + /** Maximum size */ + private transient int maxSize; + /** Scan behaviour */ + private boolean scanUntilRemovable; + + /** + * Constructs a new empty map with a maximum size of 100. + */ + public LRUMap() { + this(DEFAULT_MAX_SIZE, DEFAULT_LOAD_FACTOR, false); + } + + /** + * Constructs a new, empty map with the specified maximum size. + * + * @param maxSize the maximum size of the map + * @throws IllegalArgumentException if the maximum size is less than one + */ + public LRUMap(final int maxSize) { + this(maxSize, DEFAULT_LOAD_FACTOR); + } + + /** + * Constructs a new, empty map with the specified maximum size. + * + * @param maxSize the maximum size of the map + * @param initialSize the initial size of the map + * @throws IllegalArgumentException if the maximum size is less than one + * @throws IllegalArgumentException if the initial size is negative or larger than the maximum size + * @since 4.1 + */ + public LRUMap(final int maxSize, final int initialSize) { + this(maxSize, initialSize, DEFAULT_LOAD_FACTOR); + } + + /** + * Constructs a new, empty map with the specified maximum size. + * + * @param maxSize the maximum size of the map + * @param scanUntilRemovable scan until a removeable entry is found, default false + * @throws IllegalArgumentException if the maximum size is less than one + * @since 3.1 + */ + public LRUMap(final int maxSize, final boolean scanUntilRemovable) { + this(maxSize, DEFAULT_LOAD_FACTOR, scanUntilRemovable); + } + + /** + * Constructs a new, empty map with the specified max capacity and + * load factor. + * + * @param maxSize the maximum size of the map + * @param loadFactor the load factor + * @throws IllegalArgumentException if the maximum size is less than one + * @throws IllegalArgumentException if the load factor is less than zero + */ + public LRUMap(final int maxSize, final float loadFactor) { + this(maxSize, loadFactor, false); + } + + /** + * Constructs a new, empty map with the specified max / initial capacity and + * load factor. + * + * @param maxSize the maximum size of the map + * @param initialSize the initial size of the map + * @param loadFactor the load factor + * @throws IllegalArgumentException if the maximum size is less than one + * @throws IllegalArgumentException if the initial size is negative or larger than the maximum size + * @throws IllegalArgumentException if the load factor is less than zero + * @since 4.1 + */ + public LRUMap(final int maxSize, final int initialSize, final float loadFactor) { + this(maxSize, initialSize, loadFactor, false); + } + + /** + * Constructs a new, empty map with the specified max capacity and load factor. + * + * @param maxSize the maximum size of the map + * @param loadFactor the load factor + * @param scanUntilRemovable scan until a removeable entry is found, default false + * @throws IllegalArgumentException if the maximum size is less than one + * @throws IllegalArgumentException if the load factor is less than zero + * @since 3.1 + */ + public LRUMap(final int maxSize, final float loadFactor, final boolean scanUntilRemovable) { + this(maxSize, maxSize, loadFactor, scanUntilRemovable); + } + + /** + * Constructs a new, empty map with the specified max / initial capacity and load factor. + * + * @param maxSize the maximum size of the map + * @param initialSize the initial size of the map + * @param loadFactor the load factor + * @param scanUntilRemovable scan until a removeable entry is found, default false + * @throws IllegalArgumentException if the maximum size is less than one + * @throws IllegalArgumentException if the initial size is negative or larger than the maximum size + * @throws IllegalArgumentException if the load factor is less than zero + * @since 4.1 + */ + public LRUMap(final int maxSize, + final int initialSize, + final float loadFactor, + final boolean scanUntilRemovable) { + + super(initialSize, loadFactor); + if (maxSize < 1) { + throw new IllegalArgumentException("LRUMap max size must be greater than 0"); + } + if (initialSize > maxSize) { + throw new IllegalArgumentException("LRUMap initial size must not be greather than max size"); + } + this.maxSize = maxSize; + this.scanUntilRemovable = scanUntilRemovable; + } + + /** + * Constructor copying elements from another map. + *

+ * The maximum size is set from the map's size. + * + * @param map the map to copy + * @throws NullPointerException if the map is null + * @throws IllegalArgumentException if the map is empty + */ + public LRUMap(final Map map) { + this(map, false); + } + + /** + * Constructor copying elements from another map. + * + *

The maximum size is set from the map's size.

+ * + * @param map the map to copy + * @param scanUntilRemovable scan until a removeable entry is found, default false + * @throws NullPointerException if the map is null + * @throws IllegalArgumentException if the map is empty + * @since 3.1 + */ + public LRUMap(final Map map, final boolean scanUntilRemovable) { + this(map.size(), DEFAULT_LOAD_FACTOR, scanUntilRemovable); + putAll(map); + } + + //----------------------------------------------------------------------- + /** + * Gets the value mapped to the key specified. + *

+ * This operation changes the position of the key in the map to the + * most recently used position (last). + * + * @param key the key + * @return the mapped value, null if no match + */ + @Override + public V get(final Object key) { + return get(key, true); + } + + /** + * Gets the value mapped to the key specified. + *

+ * If {@code updateToMRU} is {@code true}, the position of the key in the map + * is changed to the most recently used position (last), otherwise the iteration + * order is not changed by this operation. + * + * @param key the key + * @param updateToMRU whether the key shall be updated to the + * most recently used position + * @return the mapped value, null if no match + * @since 4.1 + */ + public V get(final Object key, final boolean updateToMRU) { + final LinkEntry entry = getEntry(key); + if (entry == null) { + return null; + } + if (updateToMRU) { + moveToMRU(entry); + } + return entry.getValue(); + } + + //----------------------------------------------------------------------- + /** + * Moves an entry to the MRU position at the end of the list. + *

+ * This implementation moves the updated entry to the end of the list. + * + * @param entry the entry to update + */ + protected void moveToMRU(final LinkEntry entry) { + if (entry.after != header) { + modCount++; + // remove + if(entry.before == null) { + throw new IllegalStateException("Entry.before is null." + + " This should not occur if your keys are immutable, and you have used synchronization properly."); + } + entry.before.after = entry.after; + entry.after.before = entry.before; + // add first + entry.after = header; + entry.before = header.before; + header.before.after = entry; + header.before = entry; + } else if (entry == header) { + throw new IllegalStateException("Can't move header to MRU" + + " This should not occur if your keys are immutable, and you have used synchronization properly."); + } + } + + /** + * Updates an existing key-value mapping. + *

+ * This implementation moves the updated entry to the end of the list + * using {@link #moveToMRU(LinkEntry)}. + * + * @param entry the entry to update + * @param newValue the new value to store + */ + @Override + protected void updateEntry(final HashEntry entry, final V newValue) { + moveToMRU((LinkEntry) entry); // handles modCount + entry.setValue(newValue); + } + + /** + * Adds a new key-value mapping into this map. + *

+ * This implementation checks the LRU size and determines whether to + * discard an entry or not using {@link #removeLRU(LinkEntry)}. + *

+ * From Commons Collections 3.1 this method uses {@link #isFull()} rather + * than accessing size and maxSize directly. + * It also handles the scanUntilRemovable functionality. + * + * @param hashIndex the index into the data array to store at + * @param hashCode the hash code of the key to add + * @param key the key to add + * @param value the value to add + */ + @Override + protected void addMapping(final int hashIndex, final int hashCode, final K key, final V value) { + if (isFull()) { + LinkEntry reuse = header.after; + boolean removeLRUEntry = false; + if (scanUntilRemovable) { + while (reuse != header && reuse != null) { + if (removeLRU(reuse)) { + removeLRUEntry = true; + break; + } + reuse = reuse.after; + } + if (reuse == null) { + throw new IllegalStateException( + "Entry.after=null, header.after=" + header.after + " header.before=" + header.before + + " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + + " This should not occur if your keys are immutable, and you have used synchronization properly."); + } + } else { + removeLRUEntry = removeLRU(reuse); + } + + if (removeLRUEntry) { + if (reuse == null) { + throw new IllegalStateException( + "reuse=null, header.after=" + header.after + " header.before=" + header.before + + " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + + " This should not occur if your keys are immutable, and you have used synchronization properly."); + } + reuseMapping(reuse, hashIndex, hashCode, key, value); + } else { + super.addMapping(hashIndex, hashCode, key, value); + } + } else { + super.addMapping(hashIndex, hashCode, key, value); + } + } + + /** + * Reuses an entry by removing it and moving it to a new place in the map. + *

+ * This method uses {@link #removeEntry}, {@link #reuseEntry} and {@link #addEntry}. + * + * @param entry the entry to reuse + * @param hashIndex the index into the data array to store at + * @param hashCode the hash code of the key to add + * @param key the key to add + * @param value the value to add + */ + protected void reuseMapping(final LinkEntry entry, final int hashIndex, final int hashCode, + final K key, final V value) { + // find the entry before the entry specified in the hash table + // remember that the parameters (except the first) refer to the new entry, + // not the old one + try { + final int removeIndex = hashIndex(entry.hashCode, data.length); + final HashEntry[] tmp = data; // may protect against some sync issues + HashEntry loop = tmp[removeIndex]; + HashEntry previous = null; + while (loop != entry && loop != null) { + previous = loop; + loop = loop.next; + } + if (loop == null) { + throw new IllegalStateException( + "Entry.next=null, data[removeIndex]=" + data[removeIndex] + " previous=" + previous + + " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + + " This should not occur if your keys are immutable, and you have used synchronization properly."); + } + + // reuse the entry + modCount++; + removeEntry(entry, removeIndex, previous); + reuseEntry(entry, hashIndex, hashCode, key, value); + addEntry(entry, hashIndex); + } catch (final NullPointerException ex) { + throw new IllegalStateException( + "NPE, entry=" + entry + " entryIsHeader=" + (entry==header) + + " key=" + key + " value=" + value + " size=" + size + " maxSize=" + maxSize + + " This should not occur if your keys are immutable, and you have used synchronization properly."); + } + } + + /** + * Subclass method to control removal of the least recently used entry from the map. + *

+ * This method exists for subclasses to override. A subclass may wish to + * provide cleanup of resources when an entry is removed. For example: + *

+     * protected boolean removeLRU(LinkEntry entry) {
+     *   releaseResources(entry.getValue());  // release resources held by entry
+     *   return true;  // actually delete entry
+     * }
+     * 
+ *

+ * Alternatively, a subclass may choose to not remove the entry or selectively + * keep certain LRU entries. For example: + *

+     * protected boolean removeLRU(LinkEntry entry) {
+     *   if (entry.getKey().toString().startsWith("System.")) {
+     *     return false;  // entry not removed from LRUMap
+     *   } else {
+     *     return true;  // actually delete entry
+     *   }
+     * }
+     * 
+ * The effect of returning false is dependent on the scanUntilRemovable flag. + * If the flag is true, the next LRU entry will be passed to this method and so on + * until one returns false and is removed, or every entry in the map has been passed. + * If the scanUntilRemovable flag is false, the map will exceed the maximum size. + *

+ * NOTE: Commons Collections 3.0 passed the wrong entry to this method. + * This is fixed in version 3.1 onwards. + * + * @param entry the entry to be removed + * @return {@code true} + */ + protected boolean removeLRU(final LinkEntry entry) { + return true; + } + + //----------------------------------------------------------------------- + /** + * Returns true if this map is full and no new mappings can be added. + * + * @return true if the map is full + */ + @Override + public boolean isFull() { + return size >= maxSize; + } + + /** + * Gets the maximum size of the map (the bound). + * + * @return the maximum number of elements the map can hold + */ + @Override + public int maxSize() { + return maxSize; + } + + /** + * Whether this LRUMap will scan until a removable entry is found when the + * map is full. + * + * @return true if this map scans + * @since 3.1 + */ + public boolean isScanUntilRemovable() { + return scanUntilRemovable; + } + + //----------------------------------------------------------------------- + /** + * Clones the map without cloning the keys or values. + * + * @return a shallow clone + */ + @Override + public LRUMap clone() { + return (LRUMap) super.clone(); + } + + /** + * Write the map out using a custom routine. + * + * @param out the output stream + * @throws IOException if an error occurs while writing to the stream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + doWriteObject(out); + } + + /** + * Read the map in using a custom routine. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + doReadObject(in); + } + + /** + * Writes the data necessary for put() to work in deserialization. + * + * @param out the output stream + * @throws IOException if an error occurs while writing to the stream + */ + @Override + protected void doWriteObject(final ObjectOutputStream out) throws IOException { + out.writeInt(maxSize); + super.doWriteObject(out); + } + + /** + * Reads the data necessary for put() to work in the superclass. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + @Override + protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + maxSize = in.readInt(); + super.doReadObject(in); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/LinkedMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/LinkedMap.java index 6c6faf472c..cd8a1d0df0 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/LinkedMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/LinkedMap.java @@ -1,321 +1,321 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.AbstractList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.function.Predicate; - -/** - * A Map implementation that maintains the order of the entries. - * In this implementation order is maintained by original insertion. - *

- * This implementation improves on the JDK1.4 LinkedHashMap by adding the - * {@link MapIterator} - * functionality, additional convenience methods and allowing - * bidirectional iteration. It also implements OrderedMap. - * In addition, non-interface methods are provided to access the map by index. - *

- *

- * The orderedMapIterator() method provides direct access to a - * bidirectional iterator. The iterators from the other views can also be cast - * to OrderedIterator if required. - *

- *

- * All the available iterators can be reset back to the start by casting to - * ResettableIterator and calling reset(). - *

- *

- * The implementation is also designed to be subclassed, with lots of useful - * methods exposed. - *

- *

- * Note that LinkedMap is not synchronized and is not thread-safe. - * If you wish to use this map from multiple threads concurrently, you must use - * appropriate synchronization. The simplest approach is to wrap this map - * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw - * exceptions when accessed by concurrent threads without synchronization. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * @since 3.0 - */ -public class LinkedMap extends AbstractLinkedMap implements Serializable, Cloneable { - - /** Serialisation version */ - private static final long serialVersionUID = 9077234323521161066L; - - /** - * Constructs a new empty map with default size and load factor. - */ - public LinkedMap() { - super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD); - } - - /** - * Constructs a new, empty map with the specified initial capacity. - * - * @param initialCapacity the initial capacity - * @throws IllegalArgumentException if the initial capacity is negative - */ - public LinkedMap(final int initialCapacity) { - super(initialCapacity); - } - - /** - * Constructs a new, empty map with the specified initial capacity and - * load factor. - * - * @param initialCapacity the initial capacity - * @param loadFactor the load factor - * @throws IllegalArgumentException if the initial capacity is negative - * @throws IllegalArgumentException if the load factor is less than zero - */ - public LinkedMap(final int initialCapacity, final float loadFactor) { - super(initialCapacity, loadFactor); - } - - /** - * Constructor copying elements from another map. - * - * @param map the map to copy - * @throws NullPointerException if the map is null - */ - public LinkedMap(final Map map) { - super(map); - } - - //----------------------------------------------------------------------- - /** - * Clones the map without cloning the keys or values. - * - * @return a shallow clone - */ - @Override - public LinkedMap clone() { - return (LinkedMap) super.clone(); - } - - /** - * Write the map out using a custom routine. - * - * @param out the output stream - * @throws IOException if an error occurs while writing to the stream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - doWriteObject(out); - } - - /** - * Read the map in using a custom routine. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - doReadObject(in); - } - - //----------------------------------------------------------------------- - /** - * Gets the key at the specified index. - * - * @param index the index to retrieve - * @return the key at the specified index - * @throws IndexOutOfBoundsException if the index is invalid - */ - public K get(final int index) { - return getEntry(index).getKey(); - } - - /** - * Gets the value at the specified index. - * - * @param index the index to retrieve - * @return the value at the specified index - * @throws IndexOutOfBoundsException if the index is invalid - */ - public V getValue(final int index) { - return getEntry(index).getValue(); - } - - /** - * Gets the index of the specified key. - * - * @param key the key to find the index of - * @return the index, or -1 if not found - */ - public int indexOf(Object key) { - key = convertKey(key); - int i = 0; - for (LinkEntry entry = header.after; entry != header; entry = entry.after, i++) { - if (isEqualKey(key, entry.key)) { - return i; - } - } - return -1; - } - - /** - * Removes the element at the specified index. - * - * @param index the index of the object to remove - * @return the previous value corresponding the key, - * or null if none existed - * @throws IndexOutOfBoundsException if the index is invalid - */ - public V remove(final int index) { - return remove(get(index)); - } - - /** - * Gets an unmodifiable List view of the keys. - *

- * The returned list is unmodifiable because changes to the values of - * the list (using {@link ListIterator#set(Object)}) will - * effectively remove the value from the list and reinsert that value at - * the end of the list, which is an unexpected side effect of changing the - * value of a list. This occurs because changing the key, changes when the - * mapping is added to the map and thus where it appears in the list. - *

- * An alternative to this method is to use {@link #keySet()}. - * - * @see #keySet() - * @return The ordered list of keys. - */ - public List asList() { - return new LinkedMapList<>(this); - } - - /** - * List view of map. - */ - static class LinkedMapList extends AbstractList { - - private final LinkedMap parent; - - LinkedMapList(final LinkedMap parent) { - this.parent = parent; - } - - @Override - public int size() { - return parent.size(); - } - - @Override - public K get(final int index) { - return parent.get(index); - } - - @Override - public boolean contains(final Object obj) { - return parent.containsKey(obj); - } - - @Override - public int indexOf(final Object obj) { - return parent.indexOf(obj); - } - - @Override - public int lastIndexOf(final Object obj) { - return parent.indexOf(obj); - } - - @Override - public boolean containsAll(final Collection coll) { - return parent.keySet().containsAll(coll); - } - - @Override - public K remove(final int index) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(final Object obj) { - throw new UnsupportedOperationException(); - } - - /** - * @since 4.4 - */ - @Override - public boolean removeIf(final Predicate filter) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean retainAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public Object[] toArray() { - return parent.keySet().toArray(); - } - - @Override - public T[] toArray(final T[] array) { - return parent.keySet().toArray(array); - } - - @Override - public Iterator iterator() { - return UnmodifiableIterator.unmodifiableIterator(parent.keySet().iterator()); - } - - @Override - public ListIterator listIterator() { - return UnmodifiableListIterator.umodifiableListIterator(super.listIterator()); - } - - @Override - public ListIterator listIterator(final int fromIndex) { - return UnmodifiableListIterator.umodifiableListIterator(super.listIterator(fromIndex)); - } - - @Override - public List subList(final int fromIndexInclusive, final int toIndexExclusive) { - return UnmodifiableList.unmodifiableList(super.subList(fromIndexInclusive, toIndexExclusive)); - } - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.function.Predicate; + +/** + * A Map implementation that maintains the order of the entries. + * In this implementation order is maintained by original insertion. + *

+ * This implementation improves on the JDK1.4 LinkedHashMap by adding the + * {@link MapIterator} + * functionality, additional convenience methods and allowing + * bidirectional iteration. It also implements OrderedMap. + * In addition, non-interface methods are provided to access the map by index. + *

+ *

+ * The orderedMapIterator() method provides direct access to a + * bidirectional iterator. The iterators from the other views can also be cast + * to OrderedIterator if required. + *

+ *

+ * All the available iterators can be reset back to the start by casting to + * ResettableIterator and calling reset(). + *

+ *

+ * The implementation is also designed to be subclassed, with lots of useful + * methods exposed. + *

+ *

+ * Note that LinkedMap is not synchronized and is not thread-safe. + * If you wish to use this map from multiple threads concurrently, you must use + * appropriate synchronization. The simplest approach is to wrap this map + * using {@link java.util.Collections#synchronizedMap(Map)}. This class may throw + * exceptions when accessed by concurrent threads without synchronization. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * @since 3.0 + */ +public class LinkedMap extends AbstractLinkedMap implements Serializable, Cloneable { + + /** Serialisation version */ + private static final long serialVersionUID = 9077234323521161066L; + + /** + * Constructs a new empty map with default size and load factor. + */ + public LinkedMap() { + super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD); + } + + /** + * Constructs a new, empty map with the specified initial capacity. + * + * @param initialCapacity the initial capacity + * @throws IllegalArgumentException if the initial capacity is negative + */ + public LinkedMap(final int initialCapacity) { + super(initialCapacity); + } + + /** + * Constructs a new, empty map with the specified initial capacity and + * load factor. + * + * @param initialCapacity the initial capacity + * @param loadFactor the load factor + * @throws IllegalArgumentException if the initial capacity is negative + * @throws IllegalArgumentException if the load factor is less than zero + */ + public LinkedMap(final int initialCapacity, final float loadFactor) { + super(initialCapacity, loadFactor); + } + + /** + * Constructor copying elements from another map. + * + * @param map the map to copy + * @throws NullPointerException if the map is null + */ + public LinkedMap(final Map map) { + super(map); + } + + //----------------------------------------------------------------------- + /** + * Clones the map without cloning the keys or values. + * + * @return a shallow clone + */ + @Override + public LinkedMap clone() { + return (LinkedMap) super.clone(); + } + + /** + * Write the map out using a custom routine. + * + * @param out the output stream + * @throws IOException if an error occurs while writing to the stream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + doWriteObject(out); + } + + /** + * Read the map in using a custom routine. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + doReadObject(in); + } + + //----------------------------------------------------------------------- + /** + * Gets the key at the specified index. + * + * @param index the index to retrieve + * @return the key at the specified index + * @throws IndexOutOfBoundsException if the index is invalid + */ + public K get(final int index) { + return getEntry(index).getKey(); + } + + /** + * Gets the value at the specified index. + * + * @param index the index to retrieve + * @return the value at the specified index + * @throws IndexOutOfBoundsException if the index is invalid + */ + public V getValue(final int index) { + return getEntry(index).getValue(); + } + + /** + * Gets the index of the specified key. + * + * @param key the key to find the index of + * @return the index, or -1 if not found + */ + public int indexOf(Object key) { + key = convertKey(key); + int i = 0; + for (LinkEntry entry = header.after; entry != header; entry = entry.after, i++) { + if (isEqualKey(key, entry.key)) { + return i; + } + } + return -1; + } + + /** + * Removes the element at the specified index. + * + * @param index the index of the object to remove + * @return the previous value corresponding the key, + * or null if none existed + * @throws IndexOutOfBoundsException if the index is invalid + */ + public V remove(final int index) { + return remove(get(index)); + } + + /** + * Gets an unmodifiable List view of the keys. + *

+ * The returned list is unmodifiable because changes to the values of + * the list (using {@link ListIterator#set(Object)}) will + * effectively remove the value from the list and reinsert that value at + * the end of the list, which is an unexpected side effect of changing the + * value of a list. This occurs because changing the key, changes when the + * mapping is added to the map and thus where it appears in the list. + *

+ * An alternative to this method is to use {@link #keySet()}. + * + * @see #keySet() + * @return The ordered list of keys. + */ + public List asList() { + return new LinkedMapList<>(this); + } + + /** + * List view of map. + */ + static class LinkedMapList extends AbstractList { + + private final LinkedMap parent; + + LinkedMapList(final LinkedMap parent) { + this.parent = parent; + } + + @Override + public int size() { + return parent.size(); + } + + @Override + public K get(final int index) { + return parent.get(index); + } + + @Override + public boolean contains(final Object obj) { + return parent.containsKey(obj); + } + + @Override + public int indexOf(final Object obj) { + return parent.indexOf(obj); + } + + @Override + public int lastIndexOf(final Object obj) { + return parent.indexOf(obj); + } + + @Override + public boolean containsAll(final Collection coll) { + return parent.keySet().containsAll(coll); + } + + @Override + public K remove(final int index) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(final Object obj) { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.4 + */ + @Override + public boolean removeIf(final Predicate filter) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Object[] toArray() { + return parent.keySet().toArray(); + } + + @Override + public T[] toArray(final T[] array) { + return parent.keySet().toArray(array); + } + + @Override + public Iterator iterator() { + return UnmodifiableIterator.unmodifiableIterator(parent.keySet().iterator()); + } + + @Override + public ListIterator listIterator() { + return UnmodifiableListIterator.umodifiableListIterator(super.listIterator()); + } + + @Override + public ListIterator listIterator(final int fromIndex) { + return UnmodifiableListIterator.umodifiableListIterator(super.listIterator(fromIndex)); + } + + @Override + public List subList(final int fromIndexInclusive, final int toIndexExclusive) { + return UnmodifiableList.unmodifiableList(super.subList(fromIndexInclusive, toIndexExclusive)); + } + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ListIteratorWrapper.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ListIteratorWrapper.java index a4ae7714ad..a6bf9b5c93 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ListIteratorWrapper.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ListIteratorWrapper.java @@ -1,269 +1,269 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.NoSuchElementException; - -/** - * Converts an {@link Iterator} into a {@link ResettableListIterator}. - * For plain Iterators this is accomplished by caching the returned - * elements. This class can also be used to simply add - * {@link ResettableIterator} - * functionality to a given {@link ListIterator}. - *

- * The ListIterator interface has additional useful methods - * for navigation - previous() and the index methods. - * This class allows a regular Iterator to behave as a - * ListIterator. It achieves this by building a list internally - * of as the underlying iterator is traversed. - *

- * The optional operations of ListIterator are not supported for plain Iterators. - *

- * This class implements ResettableListIterator from Commons Collections 3.2. - * - * @since 2.1 - */ -public class ListIteratorWrapper implements ResettableListIterator { - - /** Message used when set or add are called. */ - private static final String UNSUPPORTED_OPERATION_MESSAGE = - "ListIteratorWrapper does not support optional operations of ListIterator."; - - /** Message used when set or add are called. */ - private static final String CANNOT_REMOVE_MESSAGE = "Cannot remove element at index {0}."; - - /** The underlying iterator being decorated. */ - private final Iterator iterator; - /** The list being used to cache the iterator. */ - private final List list = new ArrayList<>(); - - /** The current index of this iterator. */ - private int currentIndex = 0; - /** The current index of the wrapped iterator. */ - private int wrappedIteratorIndex = 0; - /** recall whether the wrapped iterator's "cursor" is in such a state as to allow remove() to be called */ - private boolean removeState; - - // Constructor - //------------------------------------------------------------------------- - /** - * Constructs a new ListIteratorWrapper that will wrap - * the given iterator. - * - * @param iterator the iterator to wrap - * @throws NullPointerException if the iterator is null - */ - public ListIteratorWrapper(final Iterator iterator) { - super(); - if (iterator == null) { - throw new NullPointerException("Iterator must not be null"); - } - this.iterator = iterator; - } - - // ListIterator interface - //------------------------------------------------------------------------- - /** - * Throws {@link UnsupportedOperationException} - * unless the underlying Iterator is a ListIterator. - * - * @param obj the object to add - * @throws UnsupportedOperationException if the underlying iterator is not of - * type {@link ListIterator} - */ - @Override - public void add(final E obj) throws UnsupportedOperationException { - if (iterator instanceof ListIterator) { - @SuppressWarnings("unchecked") - final ListIterator li = (ListIterator) iterator; - li.add(obj); - return; - } - throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE); - } - - /** - * Returns true if there are more elements in the iterator. - * - * @return true if there are more elements - */ - @Override - public boolean hasNext() { - if (currentIndex == wrappedIteratorIndex || iterator instanceof ListIterator) { - return iterator.hasNext(); - } - return true; - } - - /** - * Returns true if there are previous elements in the iterator. - * - * @return true if there are previous elements - */ - @Override - public boolean hasPrevious() { - if (iterator instanceof ListIterator) { - final ListIterator li = (ListIterator) iterator; - return li.hasPrevious(); - } - return currentIndex > 0; - } - - /** - * Returns the next element from the iterator. - * - * @return the next element from the iterator - * @throws NoSuchElementException if there are no more elements - */ - @Override - public E next() throws NoSuchElementException { - if (iterator instanceof ListIterator) { - return iterator.next(); - } - - if (currentIndex < wrappedIteratorIndex) { - ++currentIndex; - return list.get(currentIndex - 1); - } - - final E retval = iterator.next(); - list.add(retval); - ++currentIndex; - ++wrappedIteratorIndex; - removeState = true; - return retval; - } - - /** - * Returns the index of the next element. - * - * @return the index of the next element - */ - @Override - public int nextIndex() { - if (iterator instanceof ListIterator) { - final ListIterator li = (ListIterator) iterator; - return li.nextIndex(); - } - return currentIndex; - } - - /** - * Returns the previous element. - * - * @return the previous element - * @throws NoSuchElementException if there are no previous elements - */ - @Override - public E previous() throws NoSuchElementException { - if (iterator instanceof ListIterator) { - @SuppressWarnings("unchecked") - final ListIterator li = (ListIterator) iterator; - return li.previous(); - } - - if (currentIndex == 0) { - throw new NoSuchElementException(); - } - removeState = wrappedIteratorIndex == currentIndex; - return list.get(--currentIndex); - } - - /** - * Returns the index of the previous element. - * - * @return the index of the previous element - */ - @Override - public int previousIndex() { - if (iterator instanceof ListIterator) { - final ListIterator li = (ListIterator) iterator; - return li.previousIndex(); - } - return currentIndex - 1; - } - - /** - * Throws {@link UnsupportedOperationException} if {@link #previous()} has ever been called. - * - * @throws UnsupportedOperationException always - */ - @Override - public void remove() throws UnsupportedOperationException { - if (iterator instanceof ListIterator) { - iterator.remove(); - return; - } - int removeIndex = currentIndex; - if (currentIndex == wrappedIteratorIndex) { - --removeIndex; - } - if (!removeState || wrappedIteratorIndex - currentIndex > 1) { - throw new IllegalStateException(MessageFormat.format(CANNOT_REMOVE_MESSAGE, removeIndex)); - } - iterator.remove(); - list.remove(removeIndex); - currentIndex = removeIndex; - wrappedIteratorIndex--; - removeState = false; - } - - /** - * Throws {@link UnsupportedOperationException} - * unless the underlying Iterator is a ListIterator. - * - * @param obj the object to set - * @throws UnsupportedOperationException if the underlying iterator is not of - * type {@link ListIterator} - */ - @Override - public void set(final E obj) throws UnsupportedOperationException { - if (iterator instanceof ListIterator) { - @SuppressWarnings("unchecked") - final ListIterator li = (ListIterator) iterator; - li.set(obj); - return; - } - throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE); - } - - // ResettableIterator interface - //------------------------------------------------------------------------- - /** - * Resets this iterator back to the position at which the iterator - * was created. - * - * @since 3.2 - */ - @Override - public void reset() { - if (iterator instanceof ListIterator) { - final ListIterator li = (ListIterator) iterator; - while (li.previousIndex() >= 0) { - li.previous(); - } - return; - } - currentIndex = 0; - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +/** + * Converts an {@link Iterator} into a {@link ResettableListIterator}. + * For plain Iterators this is accomplished by caching the returned + * elements. This class can also be used to simply add + * {@link ResettableIterator} + * functionality to a given {@link ListIterator}. + *

+ * The ListIterator interface has additional useful methods + * for navigation - previous() and the index methods. + * This class allows a regular Iterator to behave as a + * ListIterator. It achieves this by building a list internally + * of as the underlying iterator is traversed. + *

+ * The optional operations of ListIterator are not supported for plain Iterators. + *

+ * This class implements ResettableListIterator from Commons Collections 3.2. + * + * @since 2.1 + */ +public class ListIteratorWrapper implements ResettableListIterator { + + /** Message used when set or add are called. */ + private static final String UNSUPPORTED_OPERATION_MESSAGE = + "ListIteratorWrapper does not support optional operations of ListIterator."; + + /** Message used when set or add are called. */ + private static final String CANNOT_REMOVE_MESSAGE = "Cannot remove element at index {0}."; + + /** The underlying iterator being decorated. */ + private final Iterator iterator; + /** The list being used to cache the iterator. */ + private final List list = new ArrayList<>(); + + /** The current index of this iterator. */ + private int currentIndex = 0; + /** The current index of the wrapped iterator. */ + private int wrappedIteratorIndex = 0; + /** recall whether the wrapped iterator's "cursor" is in such a state as to allow remove() to be called */ + private boolean removeState; + + // Constructor + //------------------------------------------------------------------------- + /** + * Constructs a new ListIteratorWrapper that will wrap + * the given iterator. + * + * @param iterator the iterator to wrap + * @throws NullPointerException if the iterator is null + */ + public ListIteratorWrapper(final Iterator iterator) { + super(); + if (iterator == null) { + throw new NullPointerException("Iterator must not be null"); + } + this.iterator = iterator; + } + + // ListIterator interface + //------------------------------------------------------------------------- + /** + * Throws {@link UnsupportedOperationException} + * unless the underlying Iterator is a ListIterator. + * + * @param obj the object to add + * @throws UnsupportedOperationException if the underlying iterator is not of + * type {@link ListIterator} + */ + @Override + public void add(final E obj) throws UnsupportedOperationException { + if (iterator instanceof ListIterator) { + @SuppressWarnings("unchecked") + final ListIterator li = (ListIterator) iterator; + li.add(obj); + return; + } + throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE); + } + + /** + * Returns true if there are more elements in the iterator. + * + * @return true if there are more elements + */ + @Override + public boolean hasNext() { + if (currentIndex == wrappedIteratorIndex || iterator instanceof ListIterator) { + return iterator.hasNext(); + } + return true; + } + + /** + * Returns true if there are previous elements in the iterator. + * + * @return true if there are previous elements + */ + @Override + public boolean hasPrevious() { + if (iterator instanceof ListIterator) { + final ListIterator li = (ListIterator) iterator; + return li.hasPrevious(); + } + return currentIndex > 0; + } + + /** + * Returns the next element from the iterator. + * + * @return the next element from the iterator + * @throws NoSuchElementException if there are no more elements + */ + @Override + public E next() throws NoSuchElementException { + if (iterator instanceof ListIterator) { + return iterator.next(); + } + + if (currentIndex < wrappedIteratorIndex) { + ++currentIndex; + return list.get(currentIndex - 1); + } + + final E retval = iterator.next(); + list.add(retval); + ++currentIndex; + ++wrappedIteratorIndex; + removeState = true; + return retval; + } + + /** + * Returns the index of the next element. + * + * @return the index of the next element + */ + @Override + public int nextIndex() { + if (iterator instanceof ListIterator) { + final ListIterator li = (ListIterator) iterator; + return li.nextIndex(); + } + return currentIndex; + } + + /** + * Returns the previous element. + * + * @return the previous element + * @throws NoSuchElementException if there are no previous elements + */ + @Override + public E previous() throws NoSuchElementException { + if (iterator instanceof ListIterator) { + @SuppressWarnings("unchecked") + final ListIterator li = (ListIterator) iterator; + return li.previous(); + } + + if (currentIndex == 0) { + throw new NoSuchElementException(); + } + removeState = wrappedIteratorIndex == currentIndex; + return list.get(--currentIndex); + } + + /** + * Returns the index of the previous element. + * + * @return the index of the previous element + */ + @Override + public int previousIndex() { + if (iterator instanceof ListIterator) { + final ListIterator li = (ListIterator) iterator; + return li.previousIndex(); + } + return currentIndex - 1; + } + + /** + * Throws {@link UnsupportedOperationException} if {@link #previous()} has ever been called. + * + * @throws UnsupportedOperationException always + */ + @Override + public void remove() throws UnsupportedOperationException { + if (iterator instanceof ListIterator) { + iterator.remove(); + return; + } + int removeIndex = currentIndex; + if (currentIndex == wrappedIteratorIndex) { + --removeIndex; + } + if (!removeState || wrappedIteratorIndex - currentIndex > 1) { + throw new IllegalStateException(MessageFormat.format(CANNOT_REMOVE_MESSAGE, removeIndex)); + } + iterator.remove(); + list.remove(removeIndex); + currentIndex = removeIndex; + wrappedIteratorIndex--; + removeState = false; + } + + /** + * Throws {@link UnsupportedOperationException} + * unless the underlying Iterator is a ListIterator. + * + * @param obj the object to set + * @throws UnsupportedOperationException if the underlying iterator is not of + * type {@link ListIterator} + */ + @Override + public void set(final E obj) throws UnsupportedOperationException { + if (iterator instanceof ListIterator) { + @SuppressWarnings("unchecked") + final ListIterator li = (ListIterator) iterator; + li.set(obj); + return; + } + throw new UnsupportedOperationException(UNSUPPORTED_OPERATION_MESSAGE); + } + + // ResettableIterator interface + //------------------------------------------------------------------------- + /** + * Resets this iterator back to the position at which the iterator + * was created. + * + * @since 3.2 + */ + @Override + public void reset() { + if (iterator instanceof ListIterator) { + final ListIterator li = (ListIterator) iterator; + while (li.previousIndex() >= 0) { + li.previous(); + } + return; + } + currentIndex = 0; + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/MapIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/MapIterator.java index 78acdf84c6..0a84354d61 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/MapIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/MapIterator.java @@ -1,114 +1,114 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Iterator; - -/** - * Defines an iterator that operates over a Map. - *

- * This iterator is a special version designed for maps. It can be more - * efficient to use this rather than an entry set iterator where the option - * is available, and it is certainly more convenient. - *

- *

- * A map that provides this interface may not hold the data internally using - * Map Entry objects, thus this interface can avoid lots of object creation. - *

- *

- * In use, this iterator iterates through the keys in the map. After each call - * to next(), the getValue() method provides direct - * access to the value. The value can also be set using setValue(). - *

- *
{@code
- * MapIterator it = map.mapIterator();
- * while (it.hasNext()) {
- *   String key = it.next();
- *   Integer value = it.getValue();
- *   it.setValue(value + 1);
- * }
- * }
- * - * @param the type of the keys in the map - * @param the type of the values in the map - * @since 3.0 - */ -public interface MapIterator extends Iterator { - - /** - * Checks to see if there are more entries still to be iterated. - * - * @return true if the iterator has more elements - */ - @Override - boolean hasNext(); - - /** - * Gets the next key from the Map. - * - * @return the next key in the iteration - * @throws java.util.NoSuchElementException if the iteration is finished - */ - @Override - K next(); - - //----------------------------------------------------------------------- - /** - * Gets the current key, which is the key returned by the last call - * to next(). - * - * @return the current key - * @throws IllegalStateException if next() has not yet been called - */ - K getKey(); - - /** - * Gets the current value, which is the value associated with the last key - * returned by next(). - * - * @return the current value - * @throws IllegalStateException if next() has not yet been called - */ - V getValue(); - - //----------------------------------------------------------------------- - /** - * Removes the last returned key from the underlying Map (optional operation). - *

- * This method can be called once per call to next(). - * - * @throws UnsupportedOperationException if remove is not supported by the map - * @throws IllegalStateException if next() has not yet been called - * @throws IllegalStateException if remove() has already been called - * since the last call to next() - */ - @Override - void remove(); - - /** - * Sets the value associated with the current key (optional operation). - * - * @param value the new value - * @return the previous value - * @throws UnsupportedOperationException if setValue is not supported by the map - * @throws IllegalStateException if next() has not yet been called - * @throws IllegalStateException if remove() has been called since the - * last call to next() - */ - V setValue(V value); - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Iterator; + +/** + * Defines an iterator that operates over a Map. + *

+ * This iterator is a special version designed for maps. It can be more + * efficient to use this rather than an entry set iterator where the option + * is available, and it is certainly more convenient. + *

+ *

+ * A map that provides this interface may not hold the data internally using + * Map Entry objects, thus this interface can avoid lots of object creation. + *

+ *

+ * In use, this iterator iterates through the keys in the map. After each call + * to next(), the getValue() method provides direct + * access to the value. The value can also be set using setValue(). + *

+ *
{@code
+ * MapIterator it = map.mapIterator();
+ * while (it.hasNext()) {
+ *   String key = it.next();
+ *   Integer value = it.getValue();
+ *   it.setValue(value + 1);
+ * }
+ * }
+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * @since 3.0 + */ +public interface MapIterator extends Iterator { + + /** + * Checks to see if there are more entries still to be iterated. + * + * @return true if the iterator has more elements + */ + @Override + boolean hasNext(); + + /** + * Gets the next key from the Map. + * + * @return the next key in the iteration + * @throws java.util.NoSuchElementException if the iteration is finished + */ + @Override + K next(); + + //----------------------------------------------------------------------- + /** + * Gets the current key, which is the key returned by the last call + * to next(). + * + * @return the current key + * @throws IllegalStateException if next() has not yet been called + */ + K getKey(); + + /** + * Gets the current value, which is the value associated with the last key + * returned by next(). + * + * @return the current value + * @throws IllegalStateException if next() has not yet been called + */ + V getValue(); + + //----------------------------------------------------------------------- + /** + * Removes the last returned key from the underlying Map (optional operation). + *

+ * This method can be called once per call to next(). + * + * @throws UnsupportedOperationException if remove is not supported by the map + * @throws IllegalStateException if next() has not yet been called + * @throws IllegalStateException if remove() has already been called + * since the last call to next() + */ + @Override + void remove(); + + /** + * Sets the value associated with the current key (optional operation). + * + * @param value the new value + * @return the previous value + * @throws UnsupportedOperationException if setValue is not supported by the map + * @throws IllegalStateException if next() has not yet been called + * @throws IllegalStateException if remove() has been called since the + * last call to next() + */ + V setValue(V value); + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedBidiMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedBidiMap.java index e4fa2668a2..314d68951c 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedBidiMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedBidiMap.java @@ -1,52 +1,52 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Defines a map that allows bidirectional lookup between key and values - * and retains and provides access to an ordering. - *

- * Implementations should allow a value to be looked up from a key and - * a key to be looked up from a value with equal performance. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * - * @since 3.0 - */ -public interface OrderedBidiMap extends BidiMap, OrderedMap { - - /** - * Gets a view of this map where the keys and values are reversed. - *

- * Changes to one map will be visible in the other and vice versa. - * This enables both directions of the map to be accessed equally. - *

- * Implementations should seek to avoid creating a new object every time this - * method is called. See AbstractMap.values() etc. Calling this - * method on the inverse map should return the original. - *

- * Implementations must return an OrderedBidiMap instance, - * usually by forwarding to inverseOrderedBidiMap(). - * - * @return an inverted bidirectional map - */ - @Override - OrderedBidiMap inverseBidiMap(); - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Defines a map that allows bidirectional lookup between key and values + * and retains and provides access to an ordering. + *

+ * Implementations should allow a value to be looked up from a key and + * a key to be looked up from a value with equal performance. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * + * @since 3.0 + */ +public interface OrderedBidiMap extends BidiMap, OrderedMap { + + /** + * Gets a view of this map where the keys and values are reversed. + *

+ * Changes to one map will be visible in the other and vice versa. + * This enables both directions of the map to be accessed equally. + *

+ * Implementations should seek to avoid creating a new object every time this + * method is called. See AbstractMap.values() etc. Calling this + * method on the inverse map should return the original. + *

+ * Implementations must return an OrderedBidiMap instance, + * usually by forwarding to inverseOrderedBidiMap(). + * + * @return an inverted bidirectional map + */ + @Override + OrderedBidiMap inverseBidiMap(); + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedIterator.java index d319b03460..0c48fe6b8a 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedIterator.java @@ -1,47 +1,47 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Iterator; - -/** - * Defines an iterator that operates over an ordered container. Subset of {@link java.util.ListIterator}. - *

- * This iterator allows both forward and reverse iteration through the container. - *

- * - * @param the type of elements returned by this iterator - * @since 3.0 - */ -public interface OrderedIterator extends Iterator { - - /** - * Checks to see if there is a previous element that can be iterated to. - * - * @return true if the iterator has a previous element - */ - boolean hasPrevious(); - - /** - * Gets the previous element from the container. - * - * @return the previous element in the iteration - * @throws java.util.NoSuchElementException if the iteration is finished - */ - E previous(); - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Iterator; + +/** + * Defines an iterator that operates over an ordered container. Subset of {@link java.util.ListIterator}. + *

+ * This iterator allows both forward and reverse iteration through the container. + *

+ * + * @param the type of elements returned by this iterator + * @since 3.0 + */ +public interface OrderedIterator extends Iterator { + + /** + * Checks to see if there is a previous element that can be iterated to. + * + * @return true if the iterator has a previous element + */ + boolean hasPrevious(); + + /** + * Gets the previous element from the container. + * + * @return the previous element in the iteration + * @throws java.util.NoSuchElementException if the iteration is finished + */ + E previous(); + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedMap.java index a910117cde..468f59d07d 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedMap.java @@ -1,73 +1,73 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Defines a map that maintains order and allows both forward and backward - * iteration through that order. - * - * @param the type of the keys in the map - * @param the type of the values in the map - * - * @since 3.0 - */ -public interface OrderedMap extends IterableMap { - - /** - * Obtains an OrderedMapIterator over the map. - *

- * A ordered map iterator is an efficient way of iterating over maps - * in both directions. - * - * @return a map iterator - */ - @Override - OrderedMapIterator mapIterator(); - - /** - * Gets the first key currently in this map. - * - * @return the first key currently in this map - * @throws java.util.NoSuchElementException if this map is empty - */ - K firstKey(); - - /** - * Gets the last key currently in this map. - * - * @return the last key currently in this map - * @throws java.util.NoSuchElementException if this map is empty - */ - K lastKey(); - - /** - * Gets the next key after the one specified. - * - * @param key the key to search for next from - * @return the next key, null if no match or at end - */ - K nextKey(K key); - - /** - * Gets the previous key before the one specified. - * - * @param key the key to search for previous from - * @return the previous key, null if no match or at start - */ - K previousKey(K key); - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Defines a map that maintains order and allows both forward and backward + * iteration through that order. + * + * @param the type of the keys in the map + * @param the type of the values in the map + * + * @since 3.0 + */ +public interface OrderedMap extends IterableMap { + + /** + * Obtains an OrderedMapIterator over the map. + *

+ * A ordered map iterator is an efficient way of iterating over maps + * in both directions. + * + * @return a map iterator + */ + @Override + OrderedMapIterator mapIterator(); + + /** + * Gets the first key currently in this map. + * + * @return the first key currently in this map + * @throws java.util.NoSuchElementException if this map is empty + */ + K firstKey(); + + /** + * Gets the last key currently in this map. + * + * @return the last key currently in this map + * @throws java.util.NoSuchElementException if this map is empty + */ + K lastKey(); + + /** + * Gets the next key after the one specified. + * + * @param key the key to search for next from + * @return the next key, null if no match or at end + */ + K nextKey(K key); + + /** + * Gets the previous key before the one specified. + * + * @param key the key to search for previous from + * @return the previous key, null if no match or at start + */ + K previousKey(K key); + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedMapIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedMapIterator.java index 52196cf628..06cfd172bb 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedMapIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/OrderedMapIterator.java @@ -1,48 +1,48 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Defines an iterator that operates over an ordered Map. - *

- * This iterator allows both forward and reverse iteration through the map. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * @since 3.0 - */ -public interface OrderedMapIterator extends MapIterator, OrderedIterator { - - /** - * Checks to see if there is a previous entry that can be iterated to. - * - * @return true if the iterator has a previous element - */ - @Override - boolean hasPrevious(); - - /** - * Gets the previous key from the Map. - * - * @return the previous key in the iteration - * @throws java.util.NoSuchElementException if the iteration is finished - */ - @Override - K previous(); - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Defines an iterator that operates over an ordered Map. + *

+ * This iterator allows both forward and reverse iteration through the map. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * @since 3.0 + */ +public interface OrderedMapIterator extends MapIterator, OrderedIterator { + + /** + * Checks to see if there is a previous entry that can be iterated to. + * + * @return true if the iterator has a previous element + */ + @Override + boolean hasPrevious(); + + /** + * Gets the previous key from the Map. + * + * @return the previous key in the iteration + * @throws java.util.NoSuchElementException if the iteration is finished + */ + @Override + K previous(); + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ReferenceIdentityMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ReferenceIdentityMap.java index e96ae8843d..e138406db8 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ReferenceIdentityMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ReferenceIdentityMap.java @@ -1,255 +1,255 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.lang.ref.Reference; - -/** - * A Map implementation that allows mappings to be - * removed by the garbage collector and matches keys and values based - * on == not equals(). - *

- * When you construct a ReferenceIdentityMap, you can specify what kind - * of references are used to store the map's keys and values. - * If non-hard references are used, then the garbage collector can remove - * mappings if a key or value becomes unreachable, or if the JVM's memory is - * running low. For information on how the different reference types behave, - * see {@link Reference}. - *

- *

- * Different types of references can be specified for keys and values. - * The default constructor uses hard keys and soft values, providing a - * memory-sensitive cache. - *

- *

- * This map is similar to - * {@link ReferenceMap ReferenceMap}. - * It differs in that keys and values in this class are compared using ==. - *

- *

- * This map will violate the detail of various Map and map view contracts. - * As a general rule, don't compare this map to other maps. - *

- *

- * This {@link java.util.Map Map} implementation does not allow null elements. - * Attempting to add a null key or value to the map will raise a NullPointerException. - *

- *

- * This implementation is not synchronized. - * You can use {@link java.util.Collections#synchronizedMap} to - * provide synchronized access to a ReferenceIdentityMap. - * Remember that synchronization will not stop the garbage collector removing entries. - *

- *

- * All the available iterators can be reset back to the start by casting to - * ResettableIterator and calling reset(). - *

- *

- * Note that ReferenceIdentityMap is not synchronized and is not thread-safe. - * If you wish to use this map from multiple threads concurrently, you must use - * appropriate synchronization. The simplest approach is to wrap this map - * using {@link java.util.Collections#synchronizedMap}. This class may throw - * exceptions when accessed by concurrent threads without synchronization. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * - * @see Reference - * @since 3.0 (previously in main package v2.1) - */ -public class ReferenceIdentityMap extends AbstractReferenceMap implements Serializable { - - /** Serialization version */ - private static final long serialVersionUID = -1266190134568365852L; - - /** - * Constructs a new ReferenceIdentityMap that will - * use hard references to keys and soft references to values. - */ - public ReferenceIdentityMap() { - super(ReferenceStrength.HARD, ReferenceStrength.SOFT, DEFAULT_CAPACITY, - DEFAULT_LOAD_FACTOR, false); - } - - /** - * Constructs a new ReferenceIdentityMap that will - * use the specified types of references. - * - * @param keyType the type of reference to use for keys; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param valueType the type of reference to use for values; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - */ - public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType) { - super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false); - } - - /** - * Constructs a new ReferenceIdentityMap that will - * use the specified types of references. - * - * @param keyType the type of reference to use for keys; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param valueType the type of reference to use for values; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param purgeValues should the value be automatically purged when the - * key is garbage collected - */ - public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType, - final boolean purgeValues) { - super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, purgeValues); - } - - /** - * Constructs a new ReferenceIdentityMap with the - * specified reference types, load factor and initial capacity. - * - * @param keyType the type of reference to use for keys; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param valueType the type of reference to use for values; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param capacity the initial capacity for the map - * @param loadFactor the load factor for the map - */ - public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType, - final int capacity, final float loadFactor) { - super(keyType, valueType, capacity, loadFactor, false); - } - - /** - * Constructs a new ReferenceIdentityMap with the - * specified reference types, load factor and initial capacity. - * - * @param keyType the type of reference to use for keys; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param valueType the type of reference to use for values; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param capacity the initial capacity for the map - * @param loadFactor the load factor for the map - * @param purgeValues should the value be automatically purged when the - * key is garbage collected - */ - public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType, - final int capacity, final float loadFactor, final boolean purgeValues) { - super(keyType, valueType, capacity, loadFactor, purgeValues); - } - - //----------------------------------------------------------------------- - /** - * Gets the hash code for the key specified. - *

- * This implementation uses the identity hash code. - * - * @param key the key to get a hash code for - * @return the hash code - */ - @Override - protected int hash(final Object key) { - return System.identityHashCode(key); - } - - /** - * Gets the hash code for a MapEntry. - *

- * This implementation uses the identity hash code. - * - * @param key the key to get a hash code for, may be null - * @param value the value to get a hash code for, may be null - * @return the hash code, as per the MapEntry specification - */ - @Override - protected int hashEntry(final Object key, final Object value) { - return System.identityHashCode(key) ^ - System.identityHashCode(value); - } - - /** - * Compares two keys for equals. - *

- * This implementation converts the key from the entry to a real reference - * before comparison and uses ==. - * - * @param key1 the first key to compare passed in from outside - * @param key2 the second key extracted from the entry via entry.key - * @return true if equal by identity - */ - @Override - protected boolean isEqualKey(final Object key1, Object key2) { - key2 = isKeyType(ReferenceStrength.HARD) ? key2 : ((Reference) key2).get(); - return key1 == key2; - } - - /** - * Compares two values for equals. - *

- * This implementation uses ==. - * - * @param value1 the first value to compare passed in from outside - * @param value2 the second value extracted from the entry via getValue() - * @return true if equal by identity - */ - @Override - protected boolean isEqualValue(final Object value1, final Object value2) { - return value1 == value2; - } - - //----------------------------------------------------------------------- - /** - * Write the map out using a custom routine. - * - * @param out the output stream - * @throws IOException if an error occurs while writing to the stream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - doWriteObject(out); - } - - /** - * Read the map in using a custom routine. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - doReadObject(in); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.ref.Reference; + +/** + * A Map implementation that allows mappings to be + * removed by the garbage collector and matches keys and values based + * on == not equals(). + *

+ * When you construct a ReferenceIdentityMap, you can specify what kind + * of references are used to store the map's keys and values. + * If non-hard references are used, then the garbage collector can remove + * mappings if a key or value becomes unreachable, or if the JVM's memory is + * running low. For information on how the different reference types behave, + * see {@link Reference}. + *

+ *

+ * Different types of references can be specified for keys and values. + * The default constructor uses hard keys and soft values, providing a + * memory-sensitive cache. + *

+ *

+ * This map is similar to + * {@link ReferenceMap ReferenceMap}. + * It differs in that keys and values in this class are compared using ==. + *

+ *

+ * This map will violate the detail of various Map and map view contracts. + * As a general rule, don't compare this map to other maps. + *

+ *

+ * This {@link java.util.Map Map} implementation does not allow null elements. + * Attempting to add a null key or value to the map will raise a NullPointerException. + *

+ *

+ * This implementation is not synchronized. + * You can use {@link java.util.Collections#synchronizedMap} to + * provide synchronized access to a ReferenceIdentityMap. + * Remember that synchronization will not stop the garbage collector removing entries. + *

+ *

+ * All the available iterators can be reset back to the start by casting to + * ResettableIterator and calling reset(). + *

+ *

+ * Note that ReferenceIdentityMap is not synchronized and is not thread-safe. + * If you wish to use this map from multiple threads concurrently, you must use + * appropriate synchronization. The simplest approach is to wrap this map + * using {@link java.util.Collections#synchronizedMap}. This class may throw + * exceptions when accessed by concurrent threads without synchronization. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * + * @see Reference + * @since 3.0 (previously in main package v2.1) + */ +public class ReferenceIdentityMap extends AbstractReferenceMap implements Serializable { + + /** Serialization version */ + private static final long serialVersionUID = -1266190134568365852L; + + /** + * Constructs a new ReferenceIdentityMap that will + * use hard references to keys and soft references to values. + */ + public ReferenceIdentityMap() { + super(ReferenceStrength.HARD, ReferenceStrength.SOFT, DEFAULT_CAPACITY, + DEFAULT_LOAD_FACTOR, false); + } + + /** + * Constructs a new ReferenceIdentityMap that will + * use the specified types of references. + * + * @param keyType the type of reference to use for keys; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param valueType the type of reference to use for values; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + */ + public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType) { + super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false); + } + + /** + * Constructs a new ReferenceIdentityMap that will + * use the specified types of references. + * + * @param keyType the type of reference to use for keys; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param valueType the type of reference to use for values; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param purgeValues should the value be automatically purged when the + * key is garbage collected + */ + public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType, + final boolean purgeValues) { + super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, purgeValues); + } + + /** + * Constructs a new ReferenceIdentityMap with the + * specified reference types, load factor and initial capacity. + * + * @param keyType the type of reference to use for keys; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param valueType the type of reference to use for values; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param capacity the initial capacity for the map + * @param loadFactor the load factor for the map + */ + public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType, + final int capacity, final float loadFactor) { + super(keyType, valueType, capacity, loadFactor, false); + } + + /** + * Constructs a new ReferenceIdentityMap with the + * specified reference types, load factor and initial capacity. + * + * @param keyType the type of reference to use for keys; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param valueType the type of reference to use for values; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param capacity the initial capacity for the map + * @param loadFactor the load factor for the map + * @param purgeValues should the value be automatically purged when the + * key is garbage collected + */ + public ReferenceIdentityMap(final ReferenceStrength keyType, final ReferenceStrength valueType, + final int capacity, final float loadFactor, final boolean purgeValues) { + super(keyType, valueType, capacity, loadFactor, purgeValues); + } + + //----------------------------------------------------------------------- + /** + * Gets the hash code for the key specified. + *

+ * This implementation uses the identity hash code. + * + * @param key the key to get a hash code for + * @return the hash code + */ + @Override + protected int hash(final Object key) { + return System.identityHashCode(key); + } + + /** + * Gets the hash code for a MapEntry. + *

+ * This implementation uses the identity hash code. + * + * @param key the key to get a hash code for, may be null + * @param value the value to get a hash code for, may be null + * @return the hash code, as per the MapEntry specification + */ + @Override + protected int hashEntry(final Object key, final Object value) { + return System.identityHashCode(key) ^ + System.identityHashCode(value); + } + + /** + * Compares two keys for equals. + *

+ * This implementation converts the key from the entry to a real reference + * before comparison and uses ==. + * + * @param key1 the first key to compare passed in from outside + * @param key2 the second key extracted from the entry via entry.key + * @return true if equal by identity + */ + @Override + protected boolean isEqualKey(final Object key1, Object key2) { + key2 = isKeyType(ReferenceStrength.HARD) ? key2 : ((Reference) key2).get(); + return key1 == key2; + } + + /** + * Compares two values for equals. + *

+ * This implementation uses ==. + * + * @param value1 the first value to compare passed in from outside + * @param value2 the second value extracted from the entry via getValue() + * @return true if equal by identity + */ + @Override + protected boolean isEqualValue(final Object value1, final Object value2) { + return value1 == value2; + } + + //----------------------------------------------------------------------- + /** + * Write the map out using a custom routine. + * + * @param out the output stream + * @throws IOException if an error occurs while writing to the stream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + doWriteObject(out); + } + + /** + * Read the map in using a custom routine. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + doReadObject(in); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ReferenceMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ReferenceMap.java index 5a070568d9..3b879b1f14 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ReferenceMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ReferenceMap.java @@ -1,200 +1,200 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -/** - * A Map implementation that allows mappings to be - * removed by the garbage collector. - *

- * When you construct a ReferenceMap, you can specify what kind - * of references are used to store the map's keys and values. - * If non-hard references are used, then the garbage collector can remove - * mappings if a key or value becomes unreachable, or if the JVM's memory is - * running low. For information on how the different reference types behave, - * see {@link java.lang.ref.Reference Reference}. - *

- *

- * Different types of references can be specified for keys and values. - * The keys can be configured to be weak but the values hard, - * in which case this class will behave like a - * - * WeakHashMap. However, you can also specify hard keys and - * weak values, or any other combination. The default constructor uses - * hard keys and soft values, providing a memory-sensitive cache. - *

- *

- * This map is similar to - * {@link ReferenceIdentityMap ReferenceIdentityMap}. - * It differs in that keys and values in this class are compared using equals(). - *

- *

- * This {@link java.util.Map Map} implementation does not allow null elements. - * Attempting to add a null key or value to the map will raise a NullPointerException. - *

- *

- * This implementation is not synchronized. - * You can use {@link java.util.Collections#synchronizedMap} to - * provide synchronized access to a ReferenceMap. - * Remember that synchronization will not stop the garbage collector removing entries. - *

- *

- * All the available iterators can be reset back to the start by casting to - * ResettableIterator and calling reset(). - *

- *

- * Note that ReferenceMap is not synchronized and is not thread-safe. - * If you wish to use this map from multiple threads concurrently, you must use - * appropriate synchronization. The simplest approach is to wrap this map - * using {@link java.util.Collections#synchronizedMap}. This class may throw - * exceptions when accessed by concurrent threads without synchronization. - *

- *

- * NOTE: As from Commons Collections 3.1 this map extends AbstractReferenceMap - * (previously it extended AbstractMap). As a result, the implementation is now - * extensible and provides a MapIterator. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * - * @see java.lang.ref.Reference - * @since 3.0 (previously in main package v2.1) - */ -public class ReferenceMap extends AbstractReferenceMap implements Serializable { - - /** Serialization version */ - private static final long serialVersionUID = 1555089888138299607L; - - /** - * Constructs a new ReferenceMap that will - * use hard references to keys and soft references to values. - */ - public ReferenceMap() { - super(ReferenceStrength.HARD, ReferenceStrength.SOFT, DEFAULT_CAPACITY, - DEFAULT_LOAD_FACTOR, false); - } - - /** - * Constructs a new ReferenceMap that will - * use the specified types of references. - * - * @param keyType the type of reference to use for keys; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param valueType the type of reference to use for values; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - */ - public ReferenceMap(final ReferenceStrength keyType, final ReferenceStrength valueType) { - super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false); - } - - /** - * Constructs a new ReferenceMap that will - * use the specified types of references. - * - * @param keyType the type of reference to use for keys; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param valueType the type of reference to use for values; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param purgeValues should the value be automatically purged when the - * key is garbage collected - */ - public ReferenceMap(final ReferenceStrength keyType, final ReferenceStrength valueType, final boolean purgeValues) { - super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, purgeValues); - } - - /** - * Constructs a new ReferenceMap with the - * specified reference types, load factor and initial - * capacity. - * - * @param keyType the type of reference to use for keys; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param valueType the type of reference to use for values; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param capacity the initial capacity for the map - * @param loadFactor the load factor for the map - */ - public ReferenceMap(final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity, - final float loadFactor) { - super(keyType, valueType, capacity, loadFactor, false); - } - - /** - * Constructs a new ReferenceMap with the - * specified reference types, load factor and initial - * capacity. - * - * @param keyType the type of reference to use for keys; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param valueType the type of reference to use for values; - * must be {@link ReferenceStrength#HARD HARD}, - * {@link ReferenceStrength#SOFT SOFT}, - * {@link ReferenceStrength#WEAK WEAK} - * @param capacity the initial capacity for the map - * @param loadFactor the load factor for the map - * @param purgeValues should the value be automatically purged when the - * key is garbage collected - */ - public ReferenceMap(final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity, - final float loadFactor, final boolean purgeValues) { - super(keyType, valueType, capacity, loadFactor, purgeValues); - } - - //----------------------------------------------------------------------- - /** - * Write the map out using a custom routine. - * - * @param out the output stream - * @throws IOException if an error occurs while writing to the stream - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - doWriteObject(out); - } - - /** - * Read the map in using a custom routine. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - doReadObject(in); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * A Map implementation that allows mappings to be + * removed by the garbage collector. + *

+ * When you construct a ReferenceMap, you can specify what kind + * of references are used to store the map's keys and values. + * If non-hard references are used, then the garbage collector can remove + * mappings if a key or value becomes unreachable, or if the JVM's memory is + * running low. For information on how the different reference types behave, + * see {@link java.lang.ref.Reference Reference}. + *

+ *

+ * Different types of references can be specified for keys and values. + * The keys can be configured to be weak but the values hard, + * in which case this class will behave like a + * + * WeakHashMap. However, you can also specify hard keys and + * weak values, or any other combination. The default constructor uses + * hard keys and soft values, providing a memory-sensitive cache. + *

+ *

+ * This map is similar to + * {@link ReferenceIdentityMap ReferenceIdentityMap}. + * It differs in that keys and values in this class are compared using equals(). + *

+ *

+ * This {@link java.util.Map Map} implementation does not allow null elements. + * Attempting to add a null key or value to the map will raise a NullPointerException. + *

+ *

+ * This implementation is not synchronized. + * You can use {@link java.util.Collections#synchronizedMap} to + * provide synchronized access to a ReferenceMap. + * Remember that synchronization will not stop the garbage collector removing entries. + *

+ *

+ * All the available iterators can be reset back to the start by casting to + * ResettableIterator and calling reset(). + *

+ *

+ * Note that ReferenceMap is not synchronized and is not thread-safe. + * If you wish to use this map from multiple threads concurrently, you must use + * appropriate synchronization. The simplest approach is to wrap this map + * using {@link java.util.Collections#synchronizedMap}. This class may throw + * exceptions when accessed by concurrent threads without synchronization. + *

+ *

+ * NOTE: As from Commons Collections 3.1 this map extends AbstractReferenceMap + * (previously it extended AbstractMap). As a result, the implementation is now + * extensible and provides a MapIterator. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * + * @see java.lang.ref.Reference + * @since 3.0 (previously in main package v2.1) + */ +public class ReferenceMap extends AbstractReferenceMap implements Serializable { + + /** Serialization version */ + private static final long serialVersionUID = 1555089888138299607L; + + /** + * Constructs a new ReferenceMap that will + * use hard references to keys and soft references to values. + */ + public ReferenceMap() { + super(ReferenceStrength.HARD, ReferenceStrength.SOFT, DEFAULT_CAPACITY, + DEFAULT_LOAD_FACTOR, false); + } + + /** + * Constructs a new ReferenceMap that will + * use the specified types of references. + * + * @param keyType the type of reference to use for keys; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param valueType the type of reference to use for values; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + */ + public ReferenceMap(final ReferenceStrength keyType, final ReferenceStrength valueType) { + super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false); + } + + /** + * Constructs a new ReferenceMap that will + * use the specified types of references. + * + * @param keyType the type of reference to use for keys; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param valueType the type of reference to use for values; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param purgeValues should the value be automatically purged when the + * key is garbage collected + */ + public ReferenceMap(final ReferenceStrength keyType, final ReferenceStrength valueType, final boolean purgeValues) { + super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, purgeValues); + } + + /** + * Constructs a new ReferenceMap with the + * specified reference types, load factor and initial + * capacity. + * + * @param keyType the type of reference to use for keys; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param valueType the type of reference to use for values; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param capacity the initial capacity for the map + * @param loadFactor the load factor for the map + */ + public ReferenceMap(final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity, + final float loadFactor) { + super(keyType, valueType, capacity, loadFactor, false); + } + + /** + * Constructs a new ReferenceMap with the + * specified reference types, load factor and initial + * capacity. + * + * @param keyType the type of reference to use for keys; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param valueType the type of reference to use for values; + * must be {@link ReferenceStrength#HARD HARD}, + * {@link ReferenceStrength#SOFT SOFT}, + * {@link ReferenceStrength#WEAK WEAK} + * @param capacity the initial capacity for the map + * @param loadFactor the load factor for the map + * @param purgeValues should the value be automatically purged when the + * key is garbage collected + */ + public ReferenceMap(final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity, + final float loadFactor, final boolean purgeValues) { + super(keyType, valueType, capacity, loadFactor, purgeValues); + } + + //----------------------------------------------------------------------- + /** + * Write the map out using a custom routine. + * + * @param out the output stream + * @throws IOException if an error occurs while writing to the stream + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + doWriteObject(out); + } + + /** + * Read the map in using a custom routine. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + doReadObject(in); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ResettableIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ResettableIterator.java index 0542f52b73..93eac2041e 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ResettableIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ResettableIterator.java @@ -1,38 +1,38 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Iterator; - -/** - * Defines an iterator that can be reset back to an initial state. - *

- * This interface allows an iterator to be repeatedly reused. - *

- * - * @param the type of elements returned by this iterator - * @since 3.0 - */ -public interface ResettableIterator extends Iterator { - - /** - * Resets the iterator back to the position at which the iterator - * was created. - */ - void reset(); - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Iterator; + +/** + * Defines an iterator that can be reset back to an initial state. + *

+ * This interface allows an iterator to be repeatedly reused. + *

+ * + * @param the type of elements returned by this iterator + * @since 3.0 + */ +public interface ResettableIterator extends Iterator { + + /** + * Resets the iterator back to the position at which the iterator + * was created. + */ + void reset(); + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ResettableListIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ResettableListIterator.java index 8c9cb204ea..03b3702739 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ResettableListIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/ResettableListIterator.java @@ -1,32 +1,32 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.ListIterator; - -/** - * Defines a list iterator that can be reset back to an initial state. - *

- * This interface allows an iterator to be repeatedly reused. - *

- * - * @param the type of elements returned by this iterator - * @since 3.0 - */ -public interface ResettableListIterator extends ListIterator, ResettableIterator, OrderedIterator { - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.ListIterator; + +/** + * Defines a list iterator that can be reset back to an initial state. + *

+ * This interface allows an iterator to be repeatedly reused. + *

+ * + * @param the type of elements returned by this iterator + * @since 3.0 + */ +public interface ResettableListIterator extends ListIterator, ResettableIterator, OrderedIterator { + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/SortedBidiMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/SortedBidiMap.java index 0615648655..16df70129d 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/SortedBidiMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/SortedBidiMap.java @@ -1,59 +1,59 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Comparator; -import java.util.SortedMap; - -/** - * Defines a map that allows bidirectional lookup between key and values - * and retains both keys and values in sorted order. - *

- * Implementations should allow a value to be looked up from a key and - * a key to be looked up from a value with equal performance. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * @since 3.0 - */ -public interface SortedBidiMap extends OrderedBidiMap, SortedMap { - - /** - * Gets a view of this map where the keys and values are reversed. - *

- * Changes to one map will be visible in the other and vice versa. - * This enables both directions of the map to be accessed equally. - *

- * Implementations should seek to avoid creating a new object every time this - * method is called. See AbstractMap.values() etc. Calling this - * method on the inverse map should return the original. - *

- * Implementations must return a SortedBidiMap instance, - * usually by forwarding to inverseSortedBidiMap(). - * - * @return an inverted bidirectional map - */ - @Override - SortedBidiMap inverseBidiMap(); - - /** - * Get the comparator used for the values in the value-to-key map aspect. - * @return Comparator<? super V> - */ - Comparator valueComparator(); -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Comparator; +import java.util.SortedMap; + +/** + * Defines a map that allows bidirectional lookup between key and values + * and retains both keys and values in sorted order. + *

+ * Implementations should allow a value to be looked up from a key and + * a key to be looked up from a value with equal performance. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * @since 3.0 + */ +public interface SortedBidiMap extends OrderedBidiMap, SortedMap { + + /** + * Gets a view of this map where the keys and values are reversed. + *

+ * Changes to one map will be visible in the other and vice versa. + * This enables both directions of the map to be accessed equally. + *

+ * Implementations should seek to avoid creating a new object every time this + * method is called. See AbstractMap.values() etc. Calling this + * method on the inverse map should return the original. + *

+ * Implementations must return a SortedBidiMap instance, + * usually by forwarding to inverseSortedBidiMap(). + * + * @return an inverted bidirectional map + */ + @Override + SortedBidiMap inverseBidiMap(); + + /** + * Get the comparator used for the values in the value-to-key map aspect. + * @return Comparator<? super V> + */ + Comparator valueComparator(); +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/TreeBidiMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/TreeBidiMap.java index 7535317d13..840bae38ab 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/TreeBidiMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/TreeBidiMap.java @@ -1,2253 +1,2253 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.AbstractSet; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -/** - * Red-Black tree-based implementation of BidiMap where all objects added - * implement the Comparable interface. - *

- * This class guarantees that the map will be in both ascending key order - * and ascending value order, sorted according to the natural order for - * the key's and value's classes. - *

- *

- * This Map is intended for applications that need to be able to look - * up a key-value pairing by either key or value, and need to do so - * with equal efficiency. - *

- *

- * While that goal could be accomplished by taking a pair of TreeMaps - * and redirecting requests to the appropriate TreeMap (e.g., - * containsKey would be directed to the TreeMap that maps values to - * keys, containsValue would be directed to the TreeMap that maps keys - * to values), there are problems with that implementation. - * If the data contained in the TreeMaps is large, the cost of redundant - * storage becomes significant. The {@link DualTreeBidiMap} and - * {@link DualHashBidiMap} implementations use this approach. - *

- *

- * This solution keeps minimizes the data storage by holding data only once. - * The red-black algorithm is based on {@link java.util.TreeMap}, but has been modified - * to simultaneously map a tree node by key and by value. This doubles the - * cost of put operations (but so does using two TreeMaps), and nearly doubles - * the cost of remove operations (there is a savings in that the lookup of the - * node to be removed only has to be performed once). And since only one node - * contains the key and value, storage is significantly less than that - * required by two TreeMaps. - *

- *

- * The Map.Entry instances returned by the appropriate methods will - * not allow setValue() and will throw an - * UnsupportedOperationException on attempts to call that method. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * - * @since 3.0 (previously DoubleOrderedMap v2.0) - */ -public class TreeBidiMap, V extends Comparable> - implements OrderedBidiMap, Serializable { - - enum DataElement { - KEY("key"), VALUE("value"); - - private final String description; - - /** - * Create a new TreeBidiMap.DataElement. - * - * @param description the description for the element - */ - DataElement(final String description) { - this.description = description; - } - - @Override - public String toString() { - return description; - } - } - - private static final long serialVersionUID = 721969328361807L; - - private transient Node[] rootNode; - private transient int nodeCount = 0; - private transient int modifications = 0; - private transient Set keySet; - private transient Set valuesSet; - private transient Set> entrySet; - private transient Inverse inverse = null; - - //----------------------------------------------------------------------- - /** - * Constructs a new empty TreeBidiMap. - */ - @SuppressWarnings("unchecked") - public TreeBidiMap() { - super(); - rootNode = new Node[2]; - } - - /** - * Constructs a new TreeBidiMap by copying an existing Map. - * - * @param map the map to copy - * @throws ClassCastException if the keys/values in the map are - * not Comparable or are not mutually comparable - * @throws NullPointerException if any key or value in the map is null - */ - public TreeBidiMap(final Map map) { - this(); - putAll(map); - } - - //----------------------------------------------------------------------- - /** - * Returns the number of key-value mappings in this map. - * - * @return the number of key-value mappings in this map - */ - @Override - public int size() { - return nodeCount; - } - - /** - * Checks whether the map is empty or not. - * - * @return true if the map is empty - */ - @Override - public boolean isEmpty() { - return nodeCount == 0; - } - - /** - * Checks whether this map contains the a mapping for the specified key. - *

- * The key must implement Comparable. - * - * @param key key whose presence in this map is to be tested - * @return true if this map contains a mapping for the specified key - * @throws ClassCastException if the key is of an inappropriate type - * @throws NullPointerException if the key is null - */ - @Override - public boolean containsKey(final Object key) { - checkKey(key); - return lookupKey(key) != null; - } - - /** - * Checks whether this map contains the a mapping for the specified value. - *

- * The value must implement Comparable. - * - * @param value value whose presence in this map is to be tested - * @return true if this map contains a mapping for the specified value - * @throws ClassCastException if the value is of an inappropriate type - * @throws NullPointerException if the value is null - */ - @Override - public boolean containsValue(final Object value) { - checkValue(value); - return lookupValue(value) != null; - } - - /** - * Gets the value to which this map maps the specified key. - * Returns null if the map contains no mapping for this key. - *

- * The key must implement Comparable. - * - * @param key key whose associated value is to be returned - * @return the value to which this map maps the specified key, - * or null if the map contains no mapping for this key - * @throws ClassCastException if the key is of an inappropriate type - * @throws NullPointerException if the key is null - */ - @Override - public V get(final Object key) { - checkKey(key); - final Node node = lookupKey(key); - return node == null ? null : node.getValue(); - } - - /** - * Puts the key-value pair into the map, replacing any previous pair. - *

- * When adding a key-value pair, the value may already exist in the map - * against a different key. That mapping is removed, to ensure that the - * value only occurs once in the inverse map. - *

-     *  BidiMap map1 = new TreeBidiMap();
-     *  map.put("A","B");  // contains A mapped to B, as per Map
-     *  map.put("A","C");  // contains A mapped to C, as per Map
-     *
-     *  BidiMap map2 = new TreeBidiMap();
-     *  map.put("A","B");  // contains A mapped to B, as per Map
-     *  map.put("C","B");  // contains C mapped to B, key A is removed
-     * 
- *

- * Both key and value must implement Comparable. - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - * @return the previous value for the key - * @throws ClassCastException if the key is of an inappropriate type - * @throws NullPointerException if the key is null - */ - @Override - public V put(final K key, final V value) { - final V result = get(key); - doPut(key, value); - return result; - } - - /** - * Puts all the mappings from the specified map into this map. - *

- * All keys and values must implement Comparable. - * - * @param map the map to copy from - */ - @Override - public void putAll(final Map map) { - for (final Entry e : map.entrySet()) { - put(e.getKey(), e.getValue()); - } - } - - /** - * Removes the mapping for this key from this map if present. - *

- * The key must implement Comparable. - * - * @param key key whose mapping is to be removed from the map. - * @return previous value associated with specified key, - * or null if there was no mapping for key. - * @throws ClassCastException if the key is of an inappropriate type - * @throws NullPointerException if the key is null - */ - @Override - public V remove(final Object key) { - return doRemoveKey(key); - } - - /** - * Removes all mappings from this map. - */ - @Override - public void clear() { - modify(); - - nodeCount = 0; - rootNode[DataElement.KEY.ordinal()] = null; - rootNode[DataElement.VALUE.ordinal()] = null; - } - - //----------------------------------------------------------------------- - /** - * Returns the key to which this map maps the specified value. - * Returns null if the map contains no mapping for this value. - *

- * The value must implement Comparable. - * - * @param value value whose associated key is to be returned. - * @return the key to which this map maps the specified value, - * or null if the map contains no mapping for this value. - * @throws ClassCastException if the value is of an inappropriate type - * @throws NullPointerException if the value is null - */ - @Override - public K getKey(final Object value) { - checkValue(value); - final Node node = lookupValue(value); - return node == null ? null : node.getKey(); - } - - /** - * Removes the mapping for this value from this map if present. - *

- * The value must implement Comparable. - * - * @param value value whose mapping is to be removed from the map - * @return previous key associated with specified value, - * or null if there was no mapping for value. - * @throws ClassCastException if the value is of an inappropriate type - * @throws NullPointerException if the value is null - */ - @Override - public K removeValue(final Object value) { - return doRemoveValue(value); - } - - //----------------------------------------------------------------------- - /** - * Gets the first (lowest) key currently in this map. - * - * @return the first (lowest) key currently in this sorted map - * @throws NoSuchElementException if this map is empty - */ - @Override - public K firstKey() { - if (nodeCount == 0) { - throw new NoSuchElementException("Map is empty"); - } - return leastNode(rootNode[DataElement.KEY.ordinal()], DataElement.KEY).getKey(); - } - - /** - * Gets the last (highest) key currently in this map. - * - * @return the last (highest) key currently in this sorted map - * @throws NoSuchElementException if this map is empty - */ - @Override - public K lastKey() { - if (nodeCount == 0) { - throw new NoSuchElementException("Map is empty"); - } - return greatestNode(rootNode[DataElement.KEY.ordinal()], DataElement.KEY).getKey(); - } - - /** - * Gets the next key after the one specified. - *

- * The key must implement Comparable. - * - * @param key the key to search for next from - * @return the next key, null if no match or at end - */ - @Override - public K nextKey(final K key) { - checkKey(key); - final Node node = nextGreater(lookupKey(key), DataElement.KEY); - return node == null ? null : node.getKey(); - } - - /** - * Gets the previous key before the one specified. - *

- * The key must implement Comparable. - * - * @param key the key to search for previous from - * @return the previous key, null if no match or at start - */ - @Override - public K previousKey(final K key) { - checkKey(key); - final Node node = nextSmaller(lookupKey(key), DataElement.KEY); - return node == null ? null : node.getKey(); - } - - //----------------------------------------------------------------------- - /** - * Returns a set view of the keys contained in this map in key order. - *

- * The set is backed by the map, so changes to the map are reflected in - * the set, and vice-versa. If the map is modified while an iteration over - * the set is in progress, the results of the iteration are undefined. - *

- * The set supports element removal, which removes the corresponding mapping - * from the map. It does not support the add or addAll operations. - * - * @return a set view of the keys contained in this map. - */ - @Override - public Set keySet() { - if (keySet == null) { - keySet = new KeyView(DataElement.KEY); - } - return keySet; - } - - //----------------------------------------------------------------------- - /** - * Returns a set view of the values contained in this map in key order. - * The returned object can be cast to a Set. - *

- * The set is backed by the map, so changes to the map are reflected in - * the set, and vice-versa. If the map is modified while an iteration over - * the set is in progress, the results of the iteration are undefined. - *

- * The set supports element removal, which removes the corresponding mapping - * from the map. It does not support the add or addAll operations. - * - * @return a set view of the values contained in this map. - */ - @Override - public Set values() { - if (valuesSet == null) { - valuesSet = new ValueView(DataElement.KEY); - } - return valuesSet; - } - - //----------------------------------------------------------------------- - /** - * Returns a set view of the entries contained in this map in key order. - * For simple iteration through the map, the MapIterator is quicker. - *

- * The set is backed by the map, so changes to the map are reflected in - * the set, and vice-versa. If the map is modified while an iteration over - * the set is in progress, the results of the iteration are undefined. - *

- * The set supports element removal, which removes the corresponding mapping - * from the map. It does not support the add or addAll operations. - * The returned MapEntry objects do not support setValue. - * - * @return a set view of the values contained in this map. - */ - @Override - public Set> entrySet() { - if (entrySet == null) { - entrySet = new EntryView(); - } - return entrySet; - } - - //----------------------------------------------------------------------- - @Override - public OrderedMapIterator mapIterator() { - if (isEmpty()) { - return EmptyOrderedMapIterator.emptyOrderedMapIterator(); - } - return new ViewMapIterator(DataElement.KEY); - } - - //----------------------------------------------------------------------- - /** - * Gets the inverse map for comparison. - * - * @return the inverse map - */ - @Override - public OrderedBidiMap inverseBidiMap() { - if (inverse == null) { - inverse = new Inverse(); - } - return inverse; - } - - //----------------------------------------------------------------------- - /** - * Compares for equals as per the API. - * - * @param obj the object to compare to - * @return true if equal - */ - @Override - public boolean equals(final Object obj) { - return this.doEquals(obj, DataElement.KEY); - } - - /** - * Gets the hash code value for this map as per the API. - * - * @return the hash code value for this map - */ - @Override - public int hashCode() { - return this.doHashCode(DataElement.KEY); - } - - /** - * Returns a string version of this Map in standard format. - * - * @return a standard format string version of the map - */ - @Override - public String toString() { - return this.doToString(DataElement.KEY); - } - - //----------------------------------------------------------------------- - /** - * Put logic. - * - * @param key the key, always the main map key - * @param value the value, always the main map value - */ - private void doPut(final K key, final V value) { - checkKeyAndValue(key, value); - - // store previous and remove previous mappings - doRemoveKey(key); - doRemoveValue(value); - - Node node = rootNode[DataElement.KEY.ordinal()]; - if (node == null) { - // map is empty - final Node root = new Node<>(key, value); - rootNode[DataElement.KEY.ordinal()] = root; - rootNode[DataElement.VALUE.ordinal()] = root; - grow(); - - } else { - // add new mapping - while (true) { - final int cmp = compare(key, node.getKey()); - - if (cmp == 0) { - // shouldn't happen - throw new IllegalArgumentException("Cannot store a duplicate key (\"" + key + "\") in this Map"); - } else if (cmp < 0) { - if (node.getLeft(DataElement.KEY) != null) { - node = node.getLeft(DataElement.KEY); - } else { - final Node newNode = new Node<>(key, value); - - insertValue(newNode); - node.setLeft(newNode, DataElement.KEY); - newNode.setParent(node, DataElement.KEY); - doRedBlackInsert(newNode, DataElement.KEY); - grow(); - - break; - } - } else { // cmp > 0 - if (node.getRight(DataElement.KEY) != null) { - node = node.getRight(DataElement.KEY); - } else { - final Node newNode = new Node<>(key, value); - - insertValue(newNode); - node.setRight(newNode, DataElement.KEY); - newNode.setParent(node, DataElement.KEY); - doRedBlackInsert(newNode, DataElement.KEY); - grow(); - - break; - } - } - } - } - } - - private V doRemoveKey(final Object key) { - final Node node = lookupKey(key); - if (node == null) { - return null; - } - doRedBlackDelete(node); - return node.getValue(); - } - - private K doRemoveValue(final Object value) { - final Node node = lookupValue(value); - if (node == null) { - return null; - } - doRedBlackDelete(node); - return node.getKey(); - } - - /** - * do the actual lookup of a piece of data - * - * @param data the key or value to be looked up - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return the desired Node, or null if there is no mapping of the - * specified data - */ - @SuppressWarnings("unchecked") - private > Node lookup(final Object data, final DataElement dataElement) { - Node rval = null; - Node node = rootNode[dataElement.ordinal()]; - - while (node != null) { - final int cmp = compare((T) data, (T) node.getData(dataElement)); - if (cmp == 0) { - rval = node; - break; - } - node = cmp < 0 ? node.getLeft(dataElement) : node.getRight(dataElement); - } - - return rval; - } - - private Node lookupKey(final Object key) { - return this.lookup(key, DataElement.KEY); - } - - private Node lookupValue(final Object value) { - return this.lookup(value, DataElement.VALUE); - } - - /** - * get the next larger node from the specified node - * - * @param node the node to be searched from - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return the specified node - */ - private Node nextGreater(final Node node, final DataElement dataElement) { - Node rval; - if (node == null) { - rval = null; - } else if (node.getRight(dataElement) != null) { - // everything to the node's right is larger. The least of - // the right node's descendants is the next larger node - rval = leastNode(node.getRight(dataElement), dataElement); - } else { - // traverse up our ancestry until we find an ancestor that - // is null or one whose left child is our ancestor. If we - // find a null, then this node IS the largest node in the - // tree, and there is no greater node. Otherwise, we are - // the largest node in the subtree on that ancestor's left - // ... and that ancestor is the next greatest node - Node parent = node.getParent(dataElement); - Node child = node; - - while (parent != null && child == parent.getRight(dataElement)) { - child = parent; - parent = parent.getParent(dataElement); - } - rval = parent; - } - return rval; - } - - /** - * get the next larger node from the specified node - * - * @param node the node to be searched from - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return the specified node - */ - private Node nextSmaller(final Node node, final DataElement dataElement) { - Node rval; - if (node == null) { - rval = null; - } else if (node.getLeft(dataElement) != null) { - // everything to the node's left is smaller. The greatest of - // the left node's descendants is the next smaller node - rval = greatestNode(node.getLeft(dataElement), dataElement); - } else { - // traverse up our ancestry until we find an ancestor that - // is null or one whose right child is our ancestor. If we - // find a null, then this node IS the largest node in the - // tree, and there is no greater node. Otherwise, we are - // the largest node in the subtree on that ancestor's right - // ... and that ancestor is the next greatest node - Node parent = node.getParent(dataElement); - Node child = node; - - while (parent != null && child == parent.getLeft(dataElement)) { - child = parent; - parent = parent.getParent(dataElement); - } - rval = parent; - } - return rval; - } - - //----------------------------------------------------------------------- - - /** - * Compare two objects - * - * @param o1 the first object - * @param o2 the second object - * - * @return negative value if o1 < o2; 0 if o1 == o2; positive - * value if o1 > o2 - */ - private static > int compare(final T o1, final T o2) { - return o1.compareTo(o2); - } - - /** - * Find the least node from a given node. - * - * @param node the node from which we will start searching - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return the smallest node, from the specified node, in the - * specified mapping - */ - private Node leastNode(final Node node, final DataElement dataElement) { - Node rval = node; - if (rval != null) { - while (rval.getLeft(dataElement) != null) { - rval = rval.getLeft(dataElement); - } - } - return rval; - } - - /** - * Find the greatest node from a given node. - * - * @param node the node from which we will start searching - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return the greatest node, from the specified node - */ - private Node greatestNode(final Node node, final DataElement dataElement) { - Node rval = node; - if (rval != null) { - while (rval.getRight(dataElement) != null) { - rval = rval.getRight(dataElement); - } - } - return rval; - } - - /** - * copy the color from one node to another, dealing with the fact - * that one or both nodes may, in fact, be null - * - * @param from the node whose color we're copying; may be null - * @param to the node whose color we're changing; may be null - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private void copyColor(final Node from, final Node to, final DataElement dataElement) { - if (to != null) { - if (from == null) { - // by default, make it black - to.setBlack(dataElement); - } else { - to.copyColor(from, dataElement); - } - } - } - - /** - * is the specified node red? if the node does not exist, no, it's - * black, thank you - * - * @param node the node (may be null) in question - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private static boolean isRed(final Node node, final DataElement dataElement) { - return node != null && node.isRed(dataElement); - } - - /** - * is the specified black red? if the node does not exist, sure, - * it's black, thank you - * - * @param node the node (may be null) in question - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private static boolean isBlack(final Node node, final DataElement dataElement) { - return node == null || node.isBlack(dataElement); - } - - /** - * force a node (if it exists) red - * - * @param node the node (may be null) in question - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private static void makeRed(final Node node, final DataElement dataElement) { - if (node != null) { - node.setRed(dataElement); - } - } - - /** - * force a node (if it exists) black - * - * @param node the node (may be null) in question - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private static void makeBlack(final Node node, final DataElement dataElement) { - if (node != null) { - node.setBlack(dataElement); - } - } - - /** - * get a node's grandparent. mind you, the node, its parent, or - * its grandparent may not exist. no problem - * - * @param node the node (may be null) in question - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private Node getGrandParent(final Node node, final DataElement dataElement) { - return getParent(getParent(node, dataElement), dataElement); - } - - /** - * get a node's parent. mind you, the node, or its parent, may not - * exist. no problem - * - * @param node the node (may be null) in question - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private Node getParent(final Node node, final DataElement dataElement) { - return node == null ? null : node.getParent(dataElement); - } - - /** - * get a node's right child. mind you, the node may not exist. no - * problem - * - * @param node the node (may be null) in question - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private Node getRightChild(final Node node, final DataElement dataElement) { - return node == null ? null : node.getRight(dataElement); - } - - /** - * get a node's left child. mind you, the node may not exist. no - * problem - * - * @param node the node (may be null) in question - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private Node getLeftChild(final Node node, final DataElement dataElement) { - return node == null ? null : node.getLeft(dataElement); - } - - /** - * do a rotate left. standard fare in the world of balanced trees - * - * @param node the node to be rotated - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private void rotateLeft(final Node node, final DataElement dataElement) { - final Node rightChild = node.getRight(dataElement); - node.setRight(rightChild.getLeft(dataElement), dataElement); - - if (rightChild.getLeft(dataElement) != null) { - rightChild.getLeft(dataElement).setParent(node, dataElement); - } - rightChild.setParent(node.getParent(dataElement), dataElement); - - if (node.getParent(dataElement) == null) { - // node was the root ... now its right child is the root - rootNode[dataElement.ordinal()] = rightChild; - } else if (node.getParent(dataElement).getLeft(dataElement) == node) { - node.getParent(dataElement).setLeft(rightChild, dataElement); - } else { - node.getParent(dataElement).setRight(rightChild, dataElement); - } - - rightChild.setLeft(node, dataElement); - node.setParent(rightChild, dataElement); - } - - /** - * do a rotate right. standard fare in the world of balanced trees - * - * @param node the node to be rotated - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private void rotateRight(final Node node, final DataElement dataElement) { - final Node leftChild = node.getLeft(dataElement); - node.setLeft(leftChild.getRight(dataElement), dataElement); - if (leftChild.getRight(dataElement) != null) { - leftChild.getRight(dataElement).setParent(node, dataElement); - } - leftChild.setParent(node.getParent(dataElement), dataElement); - - if (node.getParent(dataElement) == null) { - // node was the root ... now its left child is the root - rootNode[dataElement.ordinal()] = leftChild; - } else if (node.getParent(dataElement).getRight(dataElement) == node) { - node.getParent(dataElement).setRight(leftChild, dataElement); - } else { - node.getParent(dataElement).setLeft(leftChild, dataElement); - } - - leftChild.setRight(node, dataElement); - node.setParent(leftChild, dataElement); - } - - /** - * complicated red-black insert stuff. Based on Sun's TreeMap - * implementation, though it's barely recognizable any more - * - * @param insertedNode the node to be inserted - * @param dataElement the KEY or VALUE int - */ - private void doRedBlackInsert(final Node insertedNode, final DataElement dataElement) { - Node currentNode = insertedNode; - makeRed(currentNode, dataElement); - - while (currentNode != null - && currentNode != rootNode[dataElement.ordinal()] - && isRed(currentNode.getParent(dataElement), dataElement)) { - if (currentNode.isLeftChild(dataElement)) { - final Node y = getRightChild(getGrandParent(currentNode, dataElement), dataElement); - - if (isRed(y, dataElement)) { - makeBlack(getParent(currentNode, dataElement), dataElement); - makeBlack(y, dataElement); - makeRed(getGrandParent(currentNode, dataElement), dataElement); - - currentNode = getGrandParent(currentNode, dataElement); - } else { - //dead code? - if (currentNode.isRightChild(dataElement)) { - currentNode = getParent(currentNode, dataElement); - - rotateLeft(currentNode, dataElement); - } - - makeBlack(getParent(currentNode, dataElement), dataElement); - makeRed(getGrandParent(currentNode, dataElement), dataElement); - - if (getGrandParent(currentNode, dataElement) != null) { - rotateRight(getGrandParent(currentNode, dataElement), dataElement); - } - } - } else { - - // just like clause above, except swap left for right - final Node y = getLeftChild(getGrandParent(currentNode, dataElement), dataElement); - - if (isRed(y, dataElement)) { - makeBlack(getParent(currentNode, dataElement), dataElement); - makeBlack(y, dataElement); - makeRed(getGrandParent(currentNode, dataElement), dataElement); - - currentNode = getGrandParent(currentNode, dataElement); - } else { - //dead code? - if (currentNode.isLeftChild(dataElement)) { - currentNode = getParent(currentNode, dataElement); - - rotateRight(currentNode, dataElement); - } - - makeBlack(getParent(currentNode, dataElement), dataElement); - makeRed(getGrandParent(currentNode, dataElement), dataElement); - - if (getGrandParent(currentNode, dataElement) != null) { - rotateLeft(getGrandParent(currentNode, dataElement), dataElement); - } - } - } - } - - makeBlack(rootNode[dataElement.ordinal()], dataElement); - } - - /** - * complicated red-black delete stuff. Based on Sun's TreeMap - * implementation, though it's barely recognizable any more - * - * @param deletedNode the node to be deleted - */ - private void doRedBlackDelete(final Node deletedNode) { - for (final DataElement dataElement : DataElement.values()) { - // if deleted node has both left and children, swap with - // the next greater node - if (deletedNode.getLeft(dataElement) != null && deletedNode.getRight(dataElement) != null) { - swapPosition(nextGreater(deletedNode, dataElement), deletedNode, dataElement); - } - - final Node replacement = deletedNode.getLeft(dataElement) != null ? - deletedNode.getLeft(dataElement) : deletedNode.getRight(dataElement); - - if (replacement != null) { - replacement.setParent(deletedNode.getParent(dataElement), dataElement); - - if (deletedNode.getParent(dataElement) == null) { - rootNode[dataElement.ordinal()] = replacement; - } else if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { - deletedNode.getParent(dataElement).setLeft(replacement, dataElement); - } else { - deletedNode.getParent(dataElement).setRight(replacement, dataElement); - } - - deletedNode.setLeft(null, dataElement); - deletedNode.setRight(null, dataElement); - deletedNode.setParent(null, dataElement); - - if (isBlack(deletedNode, dataElement)) { - doRedBlackDeleteFixup(replacement, dataElement); - } - } else { - - // replacement is null - if (deletedNode.getParent(dataElement) == null) { - - // empty tree - rootNode[dataElement.ordinal()] = null; - } else { - - // deleted node had no children - if (isBlack(deletedNode, dataElement)) { - doRedBlackDeleteFixup(deletedNode, dataElement); - } - - if (deletedNode.getParent(dataElement) != null) { - if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { - deletedNode.getParent(dataElement).setLeft(null, dataElement); - } else { - deletedNode.getParent(dataElement).setRight(null, dataElement); - } - - deletedNode.setParent(null, dataElement); - } - } - } - } - shrink(); - } - - /** - * complicated red-black delete stuff. Based on Sun's TreeMap - * implementation, though it's barely recognizable any more. This - * rebalances the tree (somewhat, as red-black trees are not - * perfectly balanced -- perfect balancing takes longer) - * - * @param replacementNode the node being replaced - * @param dataElement the KEY or VALUE int - */ - private void doRedBlackDeleteFixup(final Node replacementNode, final DataElement dataElement) { - Node currentNode = replacementNode; - - while (currentNode != rootNode[dataElement.ordinal()] && isBlack(currentNode, dataElement)) { - if (currentNode.isLeftChild(dataElement)) { - Node siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); - - if (isRed(siblingNode, dataElement)) { - makeBlack(siblingNode, dataElement); - makeRed(getParent(currentNode, dataElement), dataElement); - rotateLeft(getParent(currentNode, dataElement), dataElement); - - siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); - } - - if (isBlack(getLeftChild(siblingNode, dataElement), dataElement) - && isBlack(getRightChild(siblingNode, dataElement), dataElement)) { - makeRed(siblingNode, dataElement); - - currentNode = getParent(currentNode, dataElement); - } else { - if (isBlack(getRightChild(siblingNode, dataElement), dataElement)) { - makeBlack(getLeftChild(siblingNode, dataElement), dataElement); - makeRed(siblingNode, dataElement); - rotateRight(siblingNode, dataElement); - - siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); - } - - copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); - makeBlack(getParent(currentNode, dataElement), dataElement); - makeBlack(getRightChild(siblingNode, dataElement), dataElement); - rotateLeft(getParent(currentNode, dataElement), dataElement); - - currentNode = rootNode[dataElement.ordinal()]; - } - } else { - Node siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); - - if (isRed(siblingNode, dataElement)) { - makeBlack(siblingNode, dataElement); - makeRed(getParent(currentNode, dataElement), dataElement); - rotateRight(getParent(currentNode, dataElement), dataElement); - - siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); - } - - if (isBlack(getRightChild(siblingNode, dataElement), dataElement) - && isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { - makeRed(siblingNode, dataElement); - - currentNode = getParent(currentNode, dataElement); - } else { - if (isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { - makeBlack(getRightChild(siblingNode, dataElement), dataElement); - makeRed(siblingNode, dataElement); - rotateLeft(siblingNode, dataElement); - - siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); - } - - copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); - makeBlack(getParent(currentNode, dataElement), dataElement); - makeBlack(getLeftChild(siblingNode, dataElement), dataElement); - rotateRight(getParent(currentNode, dataElement), dataElement); - - currentNode = rootNode[dataElement.ordinal()]; - } - } - } - - makeBlack(currentNode, dataElement); - } - - /** - * swap two nodes (except for their content), taking care of - * special cases where one is the other's parent ... hey, it - * happens. - * - * @param x one node - * @param y another node - * @param dataElement the KEY or VALUE int - */ - private void swapPosition(final Node x, final Node y, final DataElement dataElement) { - // Save initial values. - final Node xFormerParent = x.getParent(dataElement); - final Node xFormerLeftChild = x.getLeft(dataElement); - final Node xFormerRightChild = x.getRight(dataElement); - final Node yFormerParent = y.getParent(dataElement); - final Node yFormerLeftChild = y.getLeft(dataElement); - final Node yFormerRightChild = y.getRight(dataElement); - final boolean xWasLeftChild = - x.getParent(dataElement) != null && x == x.getParent(dataElement).getLeft(dataElement); - final boolean yWasLeftChild = - y.getParent(dataElement) != null && y == y.getParent(dataElement).getLeft(dataElement); - - // Swap, handling special cases of one being the other's parent. - if (x == yFormerParent) { // x was y's parent - x.setParent(y, dataElement); - - if (yWasLeftChild) { - y.setLeft(x, dataElement); - y.setRight(xFormerRightChild, dataElement); - } else { - y.setRight(x, dataElement); - y.setLeft(xFormerLeftChild, dataElement); - } - } else { - x.setParent(yFormerParent, dataElement); - - if (yFormerParent != null) { - if (yWasLeftChild) { - yFormerParent.setLeft(x, dataElement); - } else { - yFormerParent.setRight(x, dataElement); - } - } - - y.setLeft(xFormerLeftChild, dataElement); - y.setRight(xFormerRightChild, dataElement); - } - - if (y == xFormerParent) { // y was x's parent - y.setParent(x, dataElement); - - if (xWasLeftChild) { - x.setLeft(y, dataElement); - x.setRight(yFormerRightChild, dataElement); - } else { - x.setRight(y, dataElement); - x.setLeft(yFormerLeftChild, dataElement); - } - } else { - y.setParent(xFormerParent, dataElement); - - if (xFormerParent != null) { - if (xWasLeftChild) { - xFormerParent.setLeft(y, dataElement); - } else { - xFormerParent.setRight(y, dataElement); - } - } - - x.setLeft(yFormerLeftChild, dataElement); - x.setRight(yFormerRightChild, dataElement); - } - - // Fix children's parent pointers - if (x.getLeft(dataElement) != null) { - x.getLeft(dataElement).setParent(x, dataElement); - } - - if (x.getRight(dataElement) != null) { - x.getRight(dataElement).setParent(x, dataElement); - } - - if (y.getLeft(dataElement) != null) { - y.getLeft(dataElement).setParent(y, dataElement); - } - - if (y.getRight(dataElement) != null) { - y.getRight(dataElement).setParent(y, dataElement); - } - - x.swapColors(y, dataElement); - - // Check if root changed - if (rootNode[dataElement.ordinal()] == x) { - rootNode[dataElement.ordinal()] = y; - } else if (rootNode[dataElement.ordinal()] == y) { - rootNode[dataElement.ordinal()] = x; - } - } - - /** - * check if an object is fit to be proper input ... has to be - * Comparable and non-null - * - * @param o the object being checked - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * - * @throws NullPointerException if o is null - * @throws ClassCastException if o is not Comparable - */ - private static void checkNonNullComparable(final Object o, final DataElement dataElement) { - if (o == null) { - throw new NullPointerException(dataElement + " cannot be null"); - } - if (!(o instanceof Comparable)) { - throw new ClassCastException(dataElement + " must be Comparable"); - } - } - - /** - * check a key for validity (non-null and implements Comparable) - * - * @param key the key to be checked - * - * @throws NullPointerException if key is null - * @throws ClassCastException if key is not Comparable - */ - private static void checkKey(final Object key) { - checkNonNullComparable(key, DataElement.KEY); - } - - /** - * check a value for validity (non-null and implements Comparable) - * - * @param value the value to be checked - * - * @throws NullPointerException if value is null - * @throws ClassCastException if value is not Comparable - */ - private static void checkValue(final Object value) { - checkNonNullComparable(value, DataElement.VALUE); - } - - /** - * check a key and a value for validity (non-null and implements - * Comparable) - * - * @param key the key to be checked - * @param value the value to be checked - * - * @throws NullPointerException if key or value is null - * @throws ClassCastException if key or value is not Comparable - */ - private static void checkKeyAndValue(final Object key, final Object value) { - checkKey(key); - checkValue(value); - } - - /** - * increment the modification count -- used to check for - * concurrent modification of the map through the map and through - * an Iterator from one of its Set or Collection views - */ - private void modify() { - modifications++; - } - - /** - * bump up the size and note that the map has changed - */ - private void grow() { - modify(); - nodeCount++; - } - - /** - * decrement the size and note that the map has changed - */ - private void shrink() { - modify(); - nodeCount--; - } - - /** - * insert a node by its value - * - * @param newNode the node to be inserted - * - * @throws IllegalArgumentException if the node already exists - * in the value mapping - */ - private void insertValue(final Node newNode) throws IllegalArgumentException { - Node node = rootNode[DataElement.VALUE.ordinal()]; - - while (true) { - final int cmp = compare(newNode.getValue(), node.getValue()); - - if (cmp == 0) { - throw new IllegalArgumentException( - "Cannot store a duplicate value (\"" + newNode.getData(DataElement.VALUE) + "\") in this Map"); - } else if (cmp < 0) { - if (node.getLeft(DataElement.VALUE) != null) { - node = node.getLeft(DataElement.VALUE); - } else { - node.setLeft(newNode, DataElement.VALUE); - newNode.setParent(node, DataElement.VALUE); - doRedBlackInsert(newNode, DataElement.VALUE); - - break; - } - } else { // cmp > 0 - if (node.getRight(DataElement.VALUE) != null) { - node = node.getRight(DataElement.VALUE); - } else { - node.setRight(newNode, DataElement.VALUE); - newNode.setParent(node, DataElement.VALUE); - doRedBlackInsert(newNode, DataElement.VALUE); - - break; - } - } - } - } - - //----------------------------------------------------------------------- - /** - * Compares for equals as per the API. - * - * @param obj the object to compare to - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return true if equal - */ - private boolean doEquals(final Object obj, final DataElement dataElement) { - if (obj == this) { - return true; - } - if (!(obj instanceof Map)) { - return false; - } - final Map other = (Map) obj; - if (other.size() != size()) { - return false; - } - - if (nodeCount > 0) { - try { - for (final MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { - final Object key = it.next(); - final Object value = it.getValue(); - if (!value.equals(other.get(key))) { - return false; - } - } - } catch (final ClassCastException | NullPointerException ex) { - return false; - } - } - return true; - } - - /** - * Gets the hash code value for this map as per the API. - * - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return the hash code value for this map - */ - private int doHashCode(final DataElement dataElement) { - int total = 0; - if (nodeCount > 0) { - for (final MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { - final Object key = it.next(); - final Object value = it.getValue(); - total += key.hashCode() ^ value.hashCode(); - } - } - return total; - } - - /** - * Gets the string form of this map as per AbstractMap. - * - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return the string form of this map - */ - private String doToString(final DataElement dataElement) { - if (nodeCount == 0) { - return "{}"; - } - final StringBuilder buf = new StringBuilder(nodeCount * 32); - buf.append('{'); - final MapIterator it = getMapIterator(dataElement); - boolean hasNext = it.hasNext(); - while (hasNext) { - final Object key = it.next(); - final Object value = it.getValue(); - buf.append(key == this ? "(this Map)" : key) - .append('=') - .append(value == this ? "(this Map)" : value); - - hasNext = it.hasNext(); - if (hasNext) { - buf.append(", "); - } - } - - buf.append('}'); - return buf.toString(); - } - - private MapIterator getMapIterator(final DataElement dataElement) { - switch (dataElement) { - case KEY: - return new ViewMapIterator(DataElement.KEY); - case VALUE: - return new InverseViewMapIterator(DataElement.VALUE); - default: - throw new IllegalArgumentException(); - } - } - - /** - * Reads the content of the stream. - * - * @param stream the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - */ - @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect - private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException{ - stream.defaultReadObject(); - rootNode = new Node[2]; - final int size = stream.readInt(); - for(int i = 0; i < size; i++){ - final K k =(K) stream.readObject(); - final V v =(V) stream.readObject(); - put(k, v); - } - } - - /** - * Writes the content to the stream for serialization. - * - * @param stream the output stream - * @throws IOException if an error occurs while writing to the stream - */ - private void writeObject(final ObjectOutputStream stream) throws IOException{ - stream.defaultWriteObject(); - stream.writeInt(this.size()); - for (final Entry entry : entrySet()) { - stream.writeObject(entry.getKey()); - stream.writeObject(entry.getValue()); - } - } - - //----------------------------------------------------------------------- - /** - * A view of this map. - */ - abstract class View extends AbstractSet { - - /** Whether to return KEY or VALUE order. */ - final DataElement orderType; - - /** - * Constructor. - * @param orderType the KEY or VALUE int for the order - */ - View(final DataElement orderType) { - super(); - this.orderType = orderType; - } - - @Override - public int size() { - return TreeBidiMap.this.size(); - } - - @Override - public void clear() { - TreeBidiMap.this.clear(); - } - } - - class KeyView extends View { - - /** - * Create a new TreeBidiMap.KeyView. - */ - public KeyView(final DataElement orderType) { - super(orderType); - } - - @Override - public Iterator iterator() { - return new ViewMapIterator(orderType); - } - - @Override - public boolean contains(final Object obj) { - checkNonNullComparable(obj, DataElement.KEY); - return lookupKey(obj) != null; - } - - @Override - public boolean remove(final Object o) { - return doRemoveKey(o) != null; - } - - } - - class ValueView extends View { - - /** - * Create a new TreeBidiMap.ValueView. - */ - public ValueView(final DataElement orderType) { - super(orderType); - } - - @Override - public Iterator iterator() { - return new InverseViewMapIterator(orderType); - } - - @Override - public boolean contains(final Object obj) { - checkNonNullComparable(obj, DataElement.VALUE); - return lookupValue(obj) != null; - } - - @Override - public boolean remove(final Object o) { - return doRemoveValue(o) != null; - } - - } - - /** - * A view of this map. - */ - class EntryView extends View> { - - EntryView() { - super(DataElement.KEY); - } - - @Override - public boolean contains(final Object obj) { - if (!(obj instanceof Map.Entry)) { - return false; - } - final Entry entry = (Entry) obj; - final Object value = entry.getValue(); - final Node node = lookupKey(entry.getKey()); - return node != null && node.getValue().equals(value); - } - - @Override - public boolean remove(final Object obj) { - if (!(obj instanceof Map.Entry)) { - return false; - } - final Entry entry = (Entry) obj; - final Object value = entry.getValue(); - final Node node = lookupKey(entry.getKey()); - if (node != null && node.getValue().equals(value)) { - doRedBlackDelete(node); - return true; - } - return false; - } - - @Override - public Iterator> iterator() { - return new ViewMapEntryIterator(); - } - } - - /** - * A view of this map. - */ - class InverseEntryView extends View> { - - InverseEntryView() { - super(DataElement.VALUE); - } - - @Override - public boolean contains(final Object obj) { - if (!(obj instanceof Map.Entry)) { - return false; - } - final Entry entry = (Entry) obj; - final Object value = entry.getValue(); - final Node node = lookupValue(entry.getKey()); - return node != null && node.getKey().equals(value); - } - - @Override - public boolean remove(final Object obj) { - if (!(obj instanceof Map.Entry)) { - return false; - } - final Entry entry = (Entry) obj; - final Object value = entry.getValue(); - final Node node = lookupValue(entry.getKey()); - if (node != null && node.getKey().equals(value)) { - doRedBlackDelete(node); - return true; - } - return false; - } - - @Override - public Iterator> iterator() { - return new InverseViewMapEntryIterator(); - } - } - - //----------------------------------------------------------------------- - /** - * An iterator over the map. - */ - abstract class ViewIterator { - - /** Whether to return KEY or VALUE order. */ - private final DataElement orderType; - /** The last node returned by the iterator. */ - Node lastReturnedNode; - /** The next node to be returned by the iterator. */ - private Node nextNode; - /** The previous node in the sequence returned by the iterator. */ - private Node previousNode; - /** The modification count. */ - private int expectedModifications; - - /** - * Constructor. - * @param orderType the KEY or VALUE int for the order - */ - ViewIterator(final DataElement orderType) { - super(); - this.orderType = orderType; - expectedModifications = modifications; - nextNode = leastNode(rootNode[orderType.ordinal()], orderType); - lastReturnedNode = null; - previousNode = null; - } - - public final boolean hasNext() { - return nextNode != null; - } - - protected Node navigateNext() { - if (nextNode == null) { - throw new NoSuchElementException(); - } - if (modifications != expectedModifications) { - throw new ConcurrentModificationException(); - } - lastReturnedNode = nextNode; - previousNode = nextNode; - nextNode = nextGreater(nextNode, orderType); - return lastReturnedNode; - } - - public boolean hasPrevious() { - return previousNode != null; - } - - protected Node navigatePrevious() { - if (previousNode == null) { - throw new NoSuchElementException(); - } - if (modifications != expectedModifications) { - throw new ConcurrentModificationException(); - } - nextNode = lastReturnedNode; - if (nextNode == null) { - nextNode = nextGreater(previousNode, orderType); - } - lastReturnedNode = previousNode; - previousNode = nextSmaller(previousNode, orderType); - return lastReturnedNode; - } - - public final void remove() { - if (lastReturnedNode == null) { - throw new IllegalStateException(); - } - if (modifications != expectedModifications) { - throw new ConcurrentModificationException(); - } - doRedBlackDelete(lastReturnedNode); - expectedModifications++; - lastReturnedNode = null; - if (nextNode == null) { - previousNode = greatestNode(rootNode[orderType.ordinal()], orderType); - } else { - previousNode = nextSmaller(nextNode, orderType); - } - } - } - - //----------------------------------------------------------------------- - /** - * An iterator over the map. - */ - class ViewMapIterator extends ViewIterator implements OrderedMapIterator { - - /** - * Constructor. - */ - ViewMapIterator(final DataElement orderType) { - super(orderType); - } - - @Override - public K getKey() { - if (lastReturnedNode == null) { - throw new IllegalStateException( - "Iterator getKey() can only be called after next() and before remove()"); - } - return lastReturnedNode.getKey(); - } - - @Override - public V getValue() { - if (lastReturnedNode == null) { - throw new IllegalStateException( - "Iterator getValue() can only be called after next() and before remove()"); - } - return lastReturnedNode.getValue(); - } - - @Override - public V setValue(final V obj) { - throw new UnsupportedOperationException(); - } - - @Override - public K next() { - return navigateNext().getKey(); - } - - @Override - public K previous() { - return navigatePrevious().getKey(); - } - } - - /** - * An iterator over the map. - */ - class InverseViewMapIterator extends ViewIterator implements OrderedMapIterator { - - /** - * Create a new TreeBidiMap.InverseViewMapIterator. - */ - public InverseViewMapIterator(final DataElement orderType) { - super(orderType); - } - - @Override - public V getKey() { - if (lastReturnedNode == null) { - throw new IllegalStateException( - "Iterator getKey() can only be called after next() and before remove()"); - } - return lastReturnedNode.getValue(); - } - - @Override - public K getValue() { - if (lastReturnedNode == null) { - throw new IllegalStateException( - "Iterator getValue() can only be called after next() and before remove()"); - } - return lastReturnedNode.getKey(); - } - - @Override - public K setValue(final K obj) { - throw new UnsupportedOperationException(); - } - - @Override - public V next() { - return navigateNext().getValue(); - } - - @Override - public V previous() { - return navigatePrevious().getValue(); - } - } - - /** - * An iterator over the map entries. - */ - class ViewMapEntryIterator extends ViewIterator implements OrderedIterator> { - - /** - * Constructor. - */ - ViewMapEntryIterator() { - super(DataElement.KEY); - } - - @Override - public Entry next() { - return navigateNext(); - } - - @Override - public Entry previous() { - return navigatePrevious(); - } - } - - /** - * An iterator over the inverse map entries. - */ - class InverseViewMapEntryIterator extends ViewIterator implements OrderedIterator> { - - /** - * Constructor. - */ - InverseViewMapEntryIterator() { - super(DataElement.VALUE); - } - - @Override - public Entry next() { - return createEntry(navigateNext()); - } - - @Override - public Entry previous() { - return createEntry(navigatePrevious()); - } - - private Entry createEntry(final Node node) { - return new UnmodifiableMapEntry<>(node.getValue(), node.getKey()); - } - } - - //----------------------------------------------------------------------- - //----------------------------------------------------------------------- - /** - * A node used to store the data. - */ - static class Node, V extends Comparable> implements Entry, KeyValue { - - private final K key; - private final V value; - private final Node[] leftNode; - private final Node[] rightNode; - private final Node[] parentNode; - private final boolean[] blackColor; - private int hashcodeValue; - private boolean calculatedHashCode; - - /** - * Make a new cell with given key and value, and with null - * links, and black (true) colors. - * - * @param key the key of this node - * @param value the value of this node - */ - @SuppressWarnings("unchecked") - Node(final K key, final V value) { - super(); - this.key = key; - this.value = value; - leftNode = new Node[2]; - rightNode = new Node[2]; - parentNode = new Node[2]; - blackColor = new boolean[] { true, true }; - calculatedHashCode = false; - } - - private Object getData(final DataElement dataElement) { - switch (dataElement) { - case KEY: - return getKey(); - case VALUE: - return getValue(); - default: - throw new IllegalArgumentException(); - } - } - - private void setLeft(final Node node, final DataElement dataElement) { - leftNode[dataElement.ordinal()] = node; - } - - private Node getLeft(final DataElement dataElement) { - return leftNode[dataElement.ordinal()]; - } - - private void setRight(final Node node, final DataElement dataElement) { - rightNode[dataElement.ordinal()] = node; - } - - private Node getRight(final DataElement dataElement) { - return rightNode[dataElement.ordinal()]; - } - - /** - * Set this node's parent node. - * - * @param node the new parent node - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private void setParent(final Node node, final DataElement dataElement) { - parentNode[dataElement.ordinal()] = node; - } - - /** - * Get the parent node. - * - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return the parent node, may be null - */ - private Node getParent(final DataElement dataElement) { - return parentNode[dataElement.ordinal()]; - } - - /** - * Exchange colors with another node. - * - * @param node the node to swap with - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private void swapColors(final Node node, final DataElement dataElement) { - // Swap colors -- old hacker's trick - blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; - node.blackColor[dataElement.ordinal()] ^= blackColor[dataElement.ordinal()]; - blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; - } - - /** - * Is this node black? - * - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return true if black (which is represented as a true boolean) - */ - private boolean isBlack(final DataElement dataElement) { - return blackColor[dataElement.ordinal()]; - } - - /** - * Is this node red? - * - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - * @return true if non-black - */ - private boolean isRed(final DataElement dataElement) { - return !blackColor[dataElement.ordinal()]; - } - - /** - * Make this node black. - * - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private void setBlack(final DataElement dataElement) { - blackColor[dataElement.ordinal()] = true; - } - - /** - * Make this node red. - * - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private void setRed(final DataElement dataElement) { - blackColor[dataElement.ordinal()] = false; - } - - /** - * Make this node the same color as another - * - * @param node the node whose color we're adopting - * @param dataElement either {@link DataElement#KEY} key} - * or the {@link DataElement#VALUE value}. - */ - private void copyColor(final Node node, final DataElement dataElement) { - blackColor[dataElement.ordinal()] = node.blackColor[dataElement.ordinal()]; - } - - private boolean isLeftChild(final DataElement dataElement) { - return parentNode[dataElement.ordinal()] != null - && parentNode[dataElement.ordinal()].leftNode[dataElement.ordinal()] == this; - } - - private boolean isRightChild(final DataElement dataElement) { - return parentNode[dataElement.ordinal()] != null - && parentNode[dataElement.ordinal()].rightNode[dataElement.ordinal()] == this; - } - - //------------------------------------------------------------------- - /** - * Gets the key. - * - * @return the key corresponding to this entry. - */ - @Override - public K getKey() { - return key; - } - - /** - * Gets the value. - * - * @return the value corresponding to this entry. - */ - @Override - public V getValue() { - return value; - } - - /** - * Optional operation that is not permitted in this implementation - * - * @param ignored this parameter is ignored. - * @return does not return - * @throws UnsupportedOperationException always - */ - @Override - public V setValue(final V ignored) throws UnsupportedOperationException { - throw new UnsupportedOperationException("Map.Entry.setValue is not supported"); - } - - /** - * Compares the specified object with this entry for equality. - * Returns true if the given object is also a map entry and - * the two entries represent the same mapping. - * - * @param obj the object to be compared for equality with this entry. - * @return true if the specified object is equal to this entry. - */ - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Map.Entry)) { - return false; - } - final Entry e = (Entry) obj; - return getKey().equals(e.getKey()) && getValue().equals(e.getValue()); - } - - /** - * @return the hash code value for this map entry. - */ - @Override - public int hashCode() { - if (!calculatedHashCode) { - hashcodeValue = getKey().hashCode() ^ getValue().hashCode(); - calculatedHashCode = true; - } - return hashcodeValue; - } - } - - //----------------------------------------------------------------------- - /** - * The inverse map implementation. - */ - class Inverse implements OrderedBidiMap { - - /** Store the keySet once created. */ - private Set inverseKeySet; - /** Store the valuesSet once created. */ - private Set inverseValuesSet; - /** Store the entrySet once created. */ - private Set> inverseEntrySet; - - @Override - public int size() { - return TreeBidiMap.this.size(); - } - - @Override - public boolean isEmpty() { - return TreeBidiMap.this.isEmpty(); - } - - @Override - public K get(final Object key) { - return TreeBidiMap.this.getKey(key); - } - - @Override - public V getKey(final Object value) { - return TreeBidiMap.this.get(value); - } - - @Override - public boolean containsKey(final Object key) { - return TreeBidiMap.this.containsValue(key); - } - - @Override - public boolean containsValue(final Object value) { - return TreeBidiMap.this.containsKey(value); - } - - @Override - public V firstKey() { - if (TreeBidiMap.this.nodeCount == 0) { - throw new NoSuchElementException("Map is empty"); - } - return leastNode(TreeBidiMap.this.rootNode[DataElement.VALUE.ordinal()], DataElement.VALUE).getValue(); - } - - @Override - public V lastKey() { - if (TreeBidiMap.this.nodeCount == 0) { - throw new NoSuchElementException("Map is empty"); - } - return greatestNode(TreeBidiMap.this.rootNode[DataElement.VALUE.ordinal()], DataElement.VALUE).getValue(); - } - - @Override - public V nextKey(final V key) { - checkKey(key); - final Node node = nextGreater(TreeBidiMap.this.lookup(key, DataElement.VALUE), DataElement.VALUE); - return node == null ? null : node.getValue(); - } - - @Override - public V previousKey(final V key) { - checkKey(key); - final Node node = TreeBidiMap.this.nextSmaller(TreeBidiMap.this.lookup(key, DataElement.VALUE), DataElement.VALUE); - return node == null ? null : node.getValue(); - } - - @Override - public K put(final V key, final K value) { - final K result = get(key); - TreeBidiMap.this.doPut(value, key); - return result; - } - - @Override - public void putAll(final Map map) { - for (final Entry e : map.entrySet()) { - put(e.getKey(), e.getValue()); - } - } - - @Override - public K remove(final Object key) { - return TreeBidiMap.this.removeValue(key); - } - - @Override - public V removeValue(final Object value) { - return TreeBidiMap.this.remove(value); - } - - @Override - public void clear() { - TreeBidiMap.this.clear(); - } - - @Override - public Set keySet() { - if (inverseKeySet == null) { - inverseKeySet = new ValueView(DataElement.VALUE); - } - return inverseKeySet; - } - - @Override - public Set values() { - if (inverseValuesSet == null) { - inverseValuesSet = new KeyView(DataElement.VALUE); - } - return inverseValuesSet; - } - - @Override - public Set> entrySet() { - if (inverseEntrySet == null) { - inverseEntrySet = new InverseEntryView(); - } - return inverseEntrySet; - } - - @Override - public OrderedMapIterator mapIterator() { - if (isEmpty()) { - return EmptyOrderedMapIterator.emptyOrderedMapIterator(); - } - return new InverseViewMapIterator(DataElement.VALUE); - } - - @Override - public OrderedBidiMap inverseBidiMap() { - return TreeBidiMap.this; - } - - @Override - public boolean equals(final Object obj) { - return TreeBidiMap.this.doEquals(obj, DataElement.VALUE); - } - - @Override - public int hashCode() { - return TreeBidiMap.this.doHashCode(DataElement.VALUE); - } - - @Override - public String toString() { - return TreeBidiMap.this.doToString(DataElement.VALUE); - } - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.AbstractSet; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * Red-Black tree-based implementation of BidiMap where all objects added + * implement the Comparable interface. + *

+ * This class guarantees that the map will be in both ascending key order + * and ascending value order, sorted according to the natural order for + * the key's and value's classes. + *

+ *

+ * This Map is intended for applications that need to be able to look + * up a key-value pairing by either key or value, and need to do so + * with equal efficiency. + *

+ *

+ * While that goal could be accomplished by taking a pair of TreeMaps + * and redirecting requests to the appropriate TreeMap (e.g., + * containsKey would be directed to the TreeMap that maps values to + * keys, containsValue would be directed to the TreeMap that maps keys + * to values), there are problems with that implementation. + * If the data contained in the TreeMaps is large, the cost of redundant + * storage becomes significant. The {@link DualTreeBidiMap} and + * {@link DualHashBidiMap} implementations use this approach. + *

+ *

+ * This solution keeps minimizes the data storage by holding data only once. + * The red-black algorithm is based on {@link java.util.TreeMap}, but has been modified + * to simultaneously map a tree node by key and by value. This doubles the + * cost of put operations (but so does using two TreeMaps), and nearly doubles + * the cost of remove operations (there is a savings in that the lookup of the + * node to be removed only has to be performed once). And since only one node + * contains the key and value, storage is significantly less than that + * required by two TreeMaps. + *

+ *

+ * The Map.Entry instances returned by the appropriate methods will + * not allow setValue() and will throw an + * UnsupportedOperationException on attempts to call that method. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * + * @since 3.0 (previously DoubleOrderedMap v2.0) + */ +public class TreeBidiMap, V extends Comparable> + implements OrderedBidiMap, Serializable { + + enum DataElement { + KEY("key"), VALUE("value"); + + private final String description; + + /** + * Create a new TreeBidiMap.DataElement. + * + * @param description the description for the element + */ + DataElement(final String description) { + this.description = description; + } + + @Override + public String toString() { + return description; + } + } + + private static final long serialVersionUID = 721969328361807L; + + private transient Node[] rootNode; + private transient int nodeCount = 0; + private transient int modifications = 0; + private transient Set keySet; + private transient Set valuesSet; + private transient Set> entrySet; + private transient Inverse inverse = null; + + //----------------------------------------------------------------------- + /** + * Constructs a new empty TreeBidiMap. + */ + @SuppressWarnings("unchecked") + public TreeBidiMap() { + super(); + rootNode = new Node[2]; + } + + /** + * Constructs a new TreeBidiMap by copying an existing Map. + * + * @param map the map to copy + * @throws ClassCastException if the keys/values in the map are + * not Comparable or are not mutually comparable + * @throws NullPointerException if any key or value in the map is null + */ + public TreeBidiMap(final Map map) { + this(); + putAll(map); + } + + //----------------------------------------------------------------------- + /** + * Returns the number of key-value mappings in this map. + * + * @return the number of key-value mappings in this map + */ + @Override + public int size() { + return nodeCount; + } + + /** + * Checks whether the map is empty or not. + * + * @return true if the map is empty + */ + @Override + public boolean isEmpty() { + return nodeCount == 0; + } + + /** + * Checks whether this map contains the a mapping for the specified key. + *

+ * The key must implement Comparable. + * + * @param key key whose presence in this map is to be tested + * @return true if this map contains a mapping for the specified key + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if the key is null + */ + @Override + public boolean containsKey(final Object key) { + checkKey(key); + return lookupKey(key) != null; + } + + /** + * Checks whether this map contains the a mapping for the specified value. + *

+ * The value must implement Comparable. + * + * @param value value whose presence in this map is to be tested + * @return true if this map contains a mapping for the specified value + * @throws ClassCastException if the value is of an inappropriate type + * @throws NullPointerException if the value is null + */ + @Override + public boolean containsValue(final Object value) { + checkValue(value); + return lookupValue(value) != null; + } + + /** + * Gets the value to which this map maps the specified key. + * Returns null if the map contains no mapping for this key. + *

+ * The key must implement Comparable. + * + * @param key key whose associated value is to be returned + * @return the value to which this map maps the specified key, + * or null if the map contains no mapping for this key + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if the key is null + */ + @Override + public V get(final Object key) { + checkKey(key); + final Node node = lookupKey(key); + return node == null ? null : node.getValue(); + } + + /** + * Puts the key-value pair into the map, replacing any previous pair. + *

+ * When adding a key-value pair, the value may already exist in the map + * against a different key. That mapping is removed, to ensure that the + * value only occurs once in the inverse map. + *

+     *  BidiMap map1 = new TreeBidiMap();
+     *  map.put("A","B");  // contains A mapped to B, as per Map
+     *  map.put("A","C");  // contains A mapped to C, as per Map
+     *
+     *  BidiMap map2 = new TreeBidiMap();
+     *  map.put("A","B");  // contains A mapped to B, as per Map
+     *  map.put("C","B");  // contains C mapped to B, key A is removed
+     * 
+ *

+ * Both key and value must implement Comparable. + * + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value for the key + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if the key is null + */ + @Override + public V put(final K key, final V value) { + final V result = get(key); + doPut(key, value); + return result; + } + + /** + * Puts all the mappings from the specified map into this map. + *

+ * All keys and values must implement Comparable. + * + * @param map the map to copy from + */ + @Override + public void putAll(final Map map) { + for (final Entry e : map.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + /** + * Removes the mapping for this key from this map if present. + *

+ * The key must implement Comparable. + * + * @param key key whose mapping is to be removed from the map. + * @return previous value associated with specified key, + * or null if there was no mapping for key. + * @throws ClassCastException if the key is of an inappropriate type + * @throws NullPointerException if the key is null + */ + @Override + public V remove(final Object key) { + return doRemoveKey(key); + } + + /** + * Removes all mappings from this map. + */ + @Override + public void clear() { + modify(); + + nodeCount = 0; + rootNode[DataElement.KEY.ordinal()] = null; + rootNode[DataElement.VALUE.ordinal()] = null; + } + + //----------------------------------------------------------------------- + /** + * Returns the key to which this map maps the specified value. + * Returns null if the map contains no mapping for this value. + *

+ * The value must implement Comparable. + * + * @param value value whose associated key is to be returned. + * @return the key to which this map maps the specified value, + * or null if the map contains no mapping for this value. + * @throws ClassCastException if the value is of an inappropriate type + * @throws NullPointerException if the value is null + */ + @Override + public K getKey(final Object value) { + checkValue(value); + final Node node = lookupValue(value); + return node == null ? null : node.getKey(); + } + + /** + * Removes the mapping for this value from this map if present. + *

+ * The value must implement Comparable. + * + * @param value value whose mapping is to be removed from the map + * @return previous key associated with specified value, + * or null if there was no mapping for value. + * @throws ClassCastException if the value is of an inappropriate type + * @throws NullPointerException if the value is null + */ + @Override + public K removeValue(final Object value) { + return doRemoveValue(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the first (lowest) key currently in this map. + * + * @return the first (lowest) key currently in this sorted map + * @throws NoSuchElementException if this map is empty + */ + @Override + public K firstKey() { + if (nodeCount == 0) { + throw new NoSuchElementException("Map is empty"); + } + return leastNode(rootNode[DataElement.KEY.ordinal()], DataElement.KEY).getKey(); + } + + /** + * Gets the last (highest) key currently in this map. + * + * @return the last (highest) key currently in this sorted map + * @throws NoSuchElementException if this map is empty + */ + @Override + public K lastKey() { + if (nodeCount == 0) { + throw new NoSuchElementException("Map is empty"); + } + return greatestNode(rootNode[DataElement.KEY.ordinal()], DataElement.KEY).getKey(); + } + + /** + * Gets the next key after the one specified. + *

+ * The key must implement Comparable. + * + * @param key the key to search for next from + * @return the next key, null if no match or at end + */ + @Override + public K nextKey(final K key) { + checkKey(key); + final Node node = nextGreater(lookupKey(key), DataElement.KEY); + return node == null ? null : node.getKey(); + } + + /** + * Gets the previous key before the one specified. + *

+ * The key must implement Comparable. + * + * @param key the key to search for previous from + * @return the previous key, null if no match or at start + */ + @Override + public K previousKey(final K key) { + checkKey(key); + final Node node = nextSmaller(lookupKey(key), DataElement.KEY); + return node == null ? null : node.getKey(); + } + + //----------------------------------------------------------------------- + /** + * Returns a set view of the keys contained in this map in key order. + *

+ * The set is backed by the map, so changes to the map are reflected in + * the set, and vice-versa. If the map is modified while an iteration over + * the set is in progress, the results of the iteration are undefined. + *

+ * The set supports element removal, which removes the corresponding mapping + * from the map. It does not support the add or addAll operations. + * + * @return a set view of the keys contained in this map. + */ + @Override + public Set keySet() { + if (keySet == null) { + keySet = new KeyView(DataElement.KEY); + } + return keySet; + } + + //----------------------------------------------------------------------- + /** + * Returns a set view of the values contained in this map in key order. + * The returned object can be cast to a Set. + *

+ * The set is backed by the map, so changes to the map are reflected in + * the set, and vice-versa. If the map is modified while an iteration over + * the set is in progress, the results of the iteration are undefined. + *

+ * The set supports element removal, which removes the corresponding mapping + * from the map. It does not support the add or addAll operations. + * + * @return a set view of the values contained in this map. + */ + @Override + public Set values() { + if (valuesSet == null) { + valuesSet = new ValueView(DataElement.KEY); + } + return valuesSet; + } + + //----------------------------------------------------------------------- + /** + * Returns a set view of the entries contained in this map in key order. + * For simple iteration through the map, the MapIterator is quicker. + *

+ * The set is backed by the map, so changes to the map are reflected in + * the set, and vice-versa. If the map is modified while an iteration over + * the set is in progress, the results of the iteration are undefined. + *

+ * The set supports element removal, which removes the corresponding mapping + * from the map. It does not support the add or addAll operations. + * The returned MapEntry objects do not support setValue. + * + * @return a set view of the values contained in this map. + */ + @Override + public Set> entrySet() { + if (entrySet == null) { + entrySet = new EntryView(); + } + return entrySet; + } + + //----------------------------------------------------------------------- + @Override + public OrderedMapIterator mapIterator() { + if (isEmpty()) { + return EmptyOrderedMapIterator.emptyOrderedMapIterator(); + } + return new ViewMapIterator(DataElement.KEY); + } + + //----------------------------------------------------------------------- + /** + * Gets the inverse map for comparison. + * + * @return the inverse map + */ + @Override + public OrderedBidiMap inverseBidiMap() { + if (inverse == null) { + inverse = new Inverse(); + } + return inverse; + } + + //----------------------------------------------------------------------- + /** + * Compares for equals as per the API. + * + * @param obj the object to compare to + * @return true if equal + */ + @Override + public boolean equals(final Object obj) { + return this.doEquals(obj, DataElement.KEY); + } + + /** + * Gets the hash code value for this map as per the API. + * + * @return the hash code value for this map + */ + @Override + public int hashCode() { + return this.doHashCode(DataElement.KEY); + } + + /** + * Returns a string version of this Map in standard format. + * + * @return a standard format string version of the map + */ + @Override + public String toString() { + return this.doToString(DataElement.KEY); + } + + //----------------------------------------------------------------------- + /** + * Put logic. + * + * @param key the key, always the main map key + * @param value the value, always the main map value + */ + private void doPut(final K key, final V value) { + checkKeyAndValue(key, value); + + // store previous and remove previous mappings + doRemoveKey(key); + doRemoveValue(value); + + Node node = rootNode[DataElement.KEY.ordinal()]; + if (node == null) { + // map is empty + final Node root = new Node<>(key, value); + rootNode[DataElement.KEY.ordinal()] = root; + rootNode[DataElement.VALUE.ordinal()] = root; + grow(); + + } else { + // add new mapping + while (true) { + final int cmp = compare(key, node.getKey()); + + if (cmp == 0) { + // shouldn't happen + throw new IllegalArgumentException("Cannot store a duplicate key (\"" + key + "\") in this Map"); + } else if (cmp < 0) { + if (node.getLeft(DataElement.KEY) != null) { + node = node.getLeft(DataElement.KEY); + } else { + final Node newNode = new Node<>(key, value); + + insertValue(newNode); + node.setLeft(newNode, DataElement.KEY); + newNode.setParent(node, DataElement.KEY); + doRedBlackInsert(newNode, DataElement.KEY); + grow(); + + break; + } + } else { // cmp > 0 + if (node.getRight(DataElement.KEY) != null) { + node = node.getRight(DataElement.KEY); + } else { + final Node newNode = new Node<>(key, value); + + insertValue(newNode); + node.setRight(newNode, DataElement.KEY); + newNode.setParent(node, DataElement.KEY); + doRedBlackInsert(newNode, DataElement.KEY); + grow(); + + break; + } + } + } + } + } + + private V doRemoveKey(final Object key) { + final Node node = lookupKey(key); + if (node == null) { + return null; + } + doRedBlackDelete(node); + return node.getValue(); + } + + private K doRemoveValue(final Object value) { + final Node node = lookupValue(value); + if (node == null) { + return null; + } + doRedBlackDelete(node); + return node.getKey(); + } + + /** + * do the actual lookup of a piece of data + * + * @param data the key or value to be looked up + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return the desired Node, or null if there is no mapping of the + * specified data + */ + @SuppressWarnings("unchecked") + private > Node lookup(final Object data, final DataElement dataElement) { + Node rval = null; + Node node = rootNode[dataElement.ordinal()]; + + while (node != null) { + final int cmp = compare((T) data, (T) node.getData(dataElement)); + if (cmp == 0) { + rval = node; + break; + } + node = cmp < 0 ? node.getLeft(dataElement) : node.getRight(dataElement); + } + + return rval; + } + + private Node lookupKey(final Object key) { + return this.lookup(key, DataElement.KEY); + } + + private Node lookupValue(final Object value) { + return this.lookup(value, DataElement.VALUE); + } + + /** + * get the next larger node from the specified node + * + * @param node the node to be searched from + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return the specified node + */ + private Node nextGreater(final Node node, final DataElement dataElement) { + Node rval; + if (node == null) { + rval = null; + } else if (node.getRight(dataElement) != null) { + // everything to the node's right is larger. The least of + // the right node's descendants is the next larger node + rval = leastNode(node.getRight(dataElement), dataElement); + } else { + // traverse up our ancestry until we find an ancestor that + // is null or one whose left child is our ancestor. If we + // find a null, then this node IS the largest node in the + // tree, and there is no greater node. Otherwise, we are + // the largest node in the subtree on that ancestor's left + // ... and that ancestor is the next greatest node + Node parent = node.getParent(dataElement); + Node child = node; + + while (parent != null && child == parent.getRight(dataElement)) { + child = parent; + parent = parent.getParent(dataElement); + } + rval = parent; + } + return rval; + } + + /** + * get the next larger node from the specified node + * + * @param node the node to be searched from + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return the specified node + */ + private Node nextSmaller(final Node node, final DataElement dataElement) { + Node rval; + if (node == null) { + rval = null; + } else if (node.getLeft(dataElement) != null) { + // everything to the node's left is smaller. The greatest of + // the left node's descendants is the next smaller node + rval = greatestNode(node.getLeft(dataElement), dataElement); + } else { + // traverse up our ancestry until we find an ancestor that + // is null or one whose right child is our ancestor. If we + // find a null, then this node IS the largest node in the + // tree, and there is no greater node. Otherwise, we are + // the largest node in the subtree on that ancestor's right + // ... and that ancestor is the next greatest node + Node parent = node.getParent(dataElement); + Node child = node; + + while (parent != null && child == parent.getLeft(dataElement)) { + child = parent; + parent = parent.getParent(dataElement); + } + rval = parent; + } + return rval; + } + + //----------------------------------------------------------------------- + + /** + * Compare two objects + * + * @param o1 the first object + * @param o2 the second object + * + * @return negative value if o1 < o2; 0 if o1 == o2; positive + * value if o1 > o2 + */ + private static > int compare(final T o1, final T o2) { + return o1.compareTo(o2); + } + + /** + * Find the least node from a given node. + * + * @param node the node from which we will start searching + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return the smallest node, from the specified node, in the + * specified mapping + */ + private Node leastNode(final Node node, final DataElement dataElement) { + Node rval = node; + if (rval != null) { + while (rval.getLeft(dataElement) != null) { + rval = rval.getLeft(dataElement); + } + } + return rval; + } + + /** + * Find the greatest node from a given node. + * + * @param node the node from which we will start searching + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return the greatest node, from the specified node + */ + private Node greatestNode(final Node node, final DataElement dataElement) { + Node rval = node; + if (rval != null) { + while (rval.getRight(dataElement) != null) { + rval = rval.getRight(dataElement); + } + } + return rval; + } + + /** + * copy the color from one node to another, dealing with the fact + * that one or both nodes may, in fact, be null + * + * @param from the node whose color we're copying; may be null + * @param to the node whose color we're changing; may be null + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private void copyColor(final Node from, final Node to, final DataElement dataElement) { + if (to != null) { + if (from == null) { + // by default, make it black + to.setBlack(dataElement); + } else { + to.copyColor(from, dataElement); + } + } + } + + /** + * is the specified node red? if the node does not exist, no, it's + * black, thank you + * + * @param node the node (may be null) in question + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private static boolean isRed(final Node node, final DataElement dataElement) { + return node != null && node.isRed(dataElement); + } + + /** + * is the specified black red? if the node does not exist, sure, + * it's black, thank you + * + * @param node the node (may be null) in question + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private static boolean isBlack(final Node node, final DataElement dataElement) { + return node == null || node.isBlack(dataElement); + } + + /** + * force a node (if it exists) red + * + * @param node the node (may be null) in question + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private static void makeRed(final Node node, final DataElement dataElement) { + if (node != null) { + node.setRed(dataElement); + } + } + + /** + * force a node (if it exists) black + * + * @param node the node (may be null) in question + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private static void makeBlack(final Node node, final DataElement dataElement) { + if (node != null) { + node.setBlack(dataElement); + } + } + + /** + * get a node's grandparent. mind you, the node, its parent, or + * its grandparent may not exist. no problem + * + * @param node the node (may be null) in question + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private Node getGrandParent(final Node node, final DataElement dataElement) { + return getParent(getParent(node, dataElement), dataElement); + } + + /** + * get a node's parent. mind you, the node, or its parent, may not + * exist. no problem + * + * @param node the node (may be null) in question + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private Node getParent(final Node node, final DataElement dataElement) { + return node == null ? null : node.getParent(dataElement); + } + + /** + * get a node's right child. mind you, the node may not exist. no + * problem + * + * @param node the node (may be null) in question + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private Node getRightChild(final Node node, final DataElement dataElement) { + return node == null ? null : node.getRight(dataElement); + } + + /** + * get a node's left child. mind you, the node may not exist. no + * problem + * + * @param node the node (may be null) in question + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private Node getLeftChild(final Node node, final DataElement dataElement) { + return node == null ? null : node.getLeft(dataElement); + } + + /** + * do a rotate left. standard fare in the world of balanced trees + * + * @param node the node to be rotated + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private void rotateLeft(final Node node, final DataElement dataElement) { + final Node rightChild = node.getRight(dataElement); + node.setRight(rightChild.getLeft(dataElement), dataElement); + + if (rightChild.getLeft(dataElement) != null) { + rightChild.getLeft(dataElement).setParent(node, dataElement); + } + rightChild.setParent(node.getParent(dataElement), dataElement); + + if (node.getParent(dataElement) == null) { + // node was the root ... now its right child is the root + rootNode[dataElement.ordinal()] = rightChild; + } else if (node.getParent(dataElement).getLeft(dataElement) == node) { + node.getParent(dataElement).setLeft(rightChild, dataElement); + } else { + node.getParent(dataElement).setRight(rightChild, dataElement); + } + + rightChild.setLeft(node, dataElement); + node.setParent(rightChild, dataElement); + } + + /** + * do a rotate right. standard fare in the world of balanced trees + * + * @param node the node to be rotated + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private void rotateRight(final Node node, final DataElement dataElement) { + final Node leftChild = node.getLeft(dataElement); + node.setLeft(leftChild.getRight(dataElement), dataElement); + if (leftChild.getRight(dataElement) != null) { + leftChild.getRight(dataElement).setParent(node, dataElement); + } + leftChild.setParent(node.getParent(dataElement), dataElement); + + if (node.getParent(dataElement) == null) { + // node was the root ... now its left child is the root + rootNode[dataElement.ordinal()] = leftChild; + } else if (node.getParent(dataElement).getRight(dataElement) == node) { + node.getParent(dataElement).setRight(leftChild, dataElement); + } else { + node.getParent(dataElement).setLeft(leftChild, dataElement); + } + + leftChild.setRight(node, dataElement); + node.setParent(leftChild, dataElement); + } + + /** + * complicated red-black insert stuff. Based on Sun's TreeMap + * implementation, though it's barely recognizable any more + * + * @param insertedNode the node to be inserted + * @param dataElement the KEY or VALUE int + */ + private void doRedBlackInsert(final Node insertedNode, final DataElement dataElement) { + Node currentNode = insertedNode; + makeRed(currentNode, dataElement); + + while (currentNode != null + && currentNode != rootNode[dataElement.ordinal()] + && isRed(currentNode.getParent(dataElement), dataElement)) { + if (currentNode.isLeftChild(dataElement)) { + final Node y = getRightChild(getGrandParent(currentNode, dataElement), dataElement); + + if (isRed(y, dataElement)) { + makeBlack(getParent(currentNode, dataElement), dataElement); + makeBlack(y, dataElement); + makeRed(getGrandParent(currentNode, dataElement), dataElement); + + currentNode = getGrandParent(currentNode, dataElement); + } else { + //dead code? + if (currentNode.isRightChild(dataElement)) { + currentNode = getParent(currentNode, dataElement); + + rotateLeft(currentNode, dataElement); + } + + makeBlack(getParent(currentNode, dataElement), dataElement); + makeRed(getGrandParent(currentNode, dataElement), dataElement); + + if (getGrandParent(currentNode, dataElement) != null) { + rotateRight(getGrandParent(currentNode, dataElement), dataElement); + } + } + } else { + + // just like clause above, except swap left for right + final Node y = getLeftChild(getGrandParent(currentNode, dataElement), dataElement); + + if (isRed(y, dataElement)) { + makeBlack(getParent(currentNode, dataElement), dataElement); + makeBlack(y, dataElement); + makeRed(getGrandParent(currentNode, dataElement), dataElement); + + currentNode = getGrandParent(currentNode, dataElement); + } else { + //dead code? + if (currentNode.isLeftChild(dataElement)) { + currentNode = getParent(currentNode, dataElement); + + rotateRight(currentNode, dataElement); + } + + makeBlack(getParent(currentNode, dataElement), dataElement); + makeRed(getGrandParent(currentNode, dataElement), dataElement); + + if (getGrandParent(currentNode, dataElement) != null) { + rotateLeft(getGrandParent(currentNode, dataElement), dataElement); + } + } + } + } + + makeBlack(rootNode[dataElement.ordinal()], dataElement); + } + + /** + * complicated red-black delete stuff. Based on Sun's TreeMap + * implementation, though it's barely recognizable any more + * + * @param deletedNode the node to be deleted + */ + private void doRedBlackDelete(final Node deletedNode) { + for (final DataElement dataElement : DataElement.values()) { + // if deleted node has both left and children, swap with + // the next greater node + if (deletedNode.getLeft(dataElement) != null && deletedNode.getRight(dataElement) != null) { + swapPosition(nextGreater(deletedNode, dataElement), deletedNode, dataElement); + } + + final Node replacement = deletedNode.getLeft(dataElement) != null ? + deletedNode.getLeft(dataElement) : deletedNode.getRight(dataElement); + + if (replacement != null) { + replacement.setParent(deletedNode.getParent(dataElement), dataElement); + + if (deletedNode.getParent(dataElement) == null) { + rootNode[dataElement.ordinal()] = replacement; + } else if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { + deletedNode.getParent(dataElement).setLeft(replacement, dataElement); + } else { + deletedNode.getParent(dataElement).setRight(replacement, dataElement); + } + + deletedNode.setLeft(null, dataElement); + deletedNode.setRight(null, dataElement); + deletedNode.setParent(null, dataElement); + + if (isBlack(deletedNode, dataElement)) { + doRedBlackDeleteFixup(replacement, dataElement); + } + } else { + + // replacement is null + if (deletedNode.getParent(dataElement) == null) { + + // empty tree + rootNode[dataElement.ordinal()] = null; + } else { + + // deleted node had no children + if (isBlack(deletedNode, dataElement)) { + doRedBlackDeleteFixup(deletedNode, dataElement); + } + + if (deletedNode.getParent(dataElement) != null) { + if (deletedNode == deletedNode.getParent(dataElement).getLeft(dataElement)) { + deletedNode.getParent(dataElement).setLeft(null, dataElement); + } else { + deletedNode.getParent(dataElement).setRight(null, dataElement); + } + + deletedNode.setParent(null, dataElement); + } + } + } + } + shrink(); + } + + /** + * complicated red-black delete stuff. Based on Sun's TreeMap + * implementation, though it's barely recognizable any more. This + * rebalances the tree (somewhat, as red-black trees are not + * perfectly balanced -- perfect balancing takes longer) + * + * @param replacementNode the node being replaced + * @param dataElement the KEY or VALUE int + */ + private void doRedBlackDeleteFixup(final Node replacementNode, final DataElement dataElement) { + Node currentNode = replacementNode; + + while (currentNode != rootNode[dataElement.ordinal()] && isBlack(currentNode, dataElement)) { + if (currentNode.isLeftChild(dataElement)) { + Node siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); + + if (isRed(siblingNode, dataElement)) { + makeBlack(siblingNode, dataElement); + makeRed(getParent(currentNode, dataElement), dataElement); + rotateLeft(getParent(currentNode, dataElement), dataElement); + + siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); + } + + if (isBlack(getLeftChild(siblingNode, dataElement), dataElement) + && isBlack(getRightChild(siblingNode, dataElement), dataElement)) { + makeRed(siblingNode, dataElement); + + currentNode = getParent(currentNode, dataElement); + } else { + if (isBlack(getRightChild(siblingNode, dataElement), dataElement)) { + makeBlack(getLeftChild(siblingNode, dataElement), dataElement); + makeRed(siblingNode, dataElement); + rotateRight(siblingNode, dataElement); + + siblingNode = getRightChild(getParent(currentNode, dataElement), dataElement); + } + + copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); + makeBlack(getParent(currentNode, dataElement), dataElement); + makeBlack(getRightChild(siblingNode, dataElement), dataElement); + rotateLeft(getParent(currentNode, dataElement), dataElement); + + currentNode = rootNode[dataElement.ordinal()]; + } + } else { + Node siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); + + if (isRed(siblingNode, dataElement)) { + makeBlack(siblingNode, dataElement); + makeRed(getParent(currentNode, dataElement), dataElement); + rotateRight(getParent(currentNode, dataElement), dataElement); + + siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); + } + + if (isBlack(getRightChild(siblingNode, dataElement), dataElement) + && isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { + makeRed(siblingNode, dataElement); + + currentNode = getParent(currentNode, dataElement); + } else { + if (isBlack(getLeftChild(siblingNode, dataElement), dataElement)) { + makeBlack(getRightChild(siblingNode, dataElement), dataElement); + makeRed(siblingNode, dataElement); + rotateLeft(siblingNode, dataElement); + + siblingNode = getLeftChild(getParent(currentNode, dataElement), dataElement); + } + + copyColor(getParent(currentNode, dataElement), siblingNode, dataElement); + makeBlack(getParent(currentNode, dataElement), dataElement); + makeBlack(getLeftChild(siblingNode, dataElement), dataElement); + rotateRight(getParent(currentNode, dataElement), dataElement); + + currentNode = rootNode[dataElement.ordinal()]; + } + } + } + + makeBlack(currentNode, dataElement); + } + + /** + * swap two nodes (except for their content), taking care of + * special cases where one is the other's parent ... hey, it + * happens. + * + * @param x one node + * @param y another node + * @param dataElement the KEY or VALUE int + */ + private void swapPosition(final Node x, final Node y, final DataElement dataElement) { + // Save initial values. + final Node xFormerParent = x.getParent(dataElement); + final Node xFormerLeftChild = x.getLeft(dataElement); + final Node xFormerRightChild = x.getRight(dataElement); + final Node yFormerParent = y.getParent(dataElement); + final Node yFormerLeftChild = y.getLeft(dataElement); + final Node yFormerRightChild = y.getRight(dataElement); + final boolean xWasLeftChild = + x.getParent(dataElement) != null && x == x.getParent(dataElement).getLeft(dataElement); + final boolean yWasLeftChild = + y.getParent(dataElement) != null && y == y.getParent(dataElement).getLeft(dataElement); + + // Swap, handling special cases of one being the other's parent. + if (x == yFormerParent) { // x was y's parent + x.setParent(y, dataElement); + + if (yWasLeftChild) { + y.setLeft(x, dataElement); + y.setRight(xFormerRightChild, dataElement); + } else { + y.setRight(x, dataElement); + y.setLeft(xFormerLeftChild, dataElement); + } + } else { + x.setParent(yFormerParent, dataElement); + + if (yFormerParent != null) { + if (yWasLeftChild) { + yFormerParent.setLeft(x, dataElement); + } else { + yFormerParent.setRight(x, dataElement); + } + } + + y.setLeft(xFormerLeftChild, dataElement); + y.setRight(xFormerRightChild, dataElement); + } + + if (y == xFormerParent) { // y was x's parent + y.setParent(x, dataElement); + + if (xWasLeftChild) { + x.setLeft(y, dataElement); + x.setRight(yFormerRightChild, dataElement); + } else { + x.setRight(y, dataElement); + x.setLeft(yFormerLeftChild, dataElement); + } + } else { + y.setParent(xFormerParent, dataElement); + + if (xFormerParent != null) { + if (xWasLeftChild) { + xFormerParent.setLeft(y, dataElement); + } else { + xFormerParent.setRight(y, dataElement); + } + } + + x.setLeft(yFormerLeftChild, dataElement); + x.setRight(yFormerRightChild, dataElement); + } + + // Fix children's parent pointers + if (x.getLeft(dataElement) != null) { + x.getLeft(dataElement).setParent(x, dataElement); + } + + if (x.getRight(dataElement) != null) { + x.getRight(dataElement).setParent(x, dataElement); + } + + if (y.getLeft(dataElement) != null) { + y.getLeft(dataElement).setParent(y, dataElement); + } + + if (y.getRight(dataElement) != null) { + y.getRight(dataElement).setParent(y, dataElement); + } + + x.swapColors(y, dataElement); + + // Check if root changed + if (rootNode[dataElement.ordinal()] == x) { + rootNode[dataElement.ordinal()] = y; + } else if (rootNode[dataElement.ordinal()] == y) { + rootNode[dataElement.ordinal()] = x; + } + } + + /** + * check if an object is fit to be proper input ... has to be + * Comparable and non-null + * + * @param o the object being checked + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * + * @throws NullPointerException if o is null + * @throws ClassCastException if o is not Comparable + */ + private static void checkNonNullComparable(final Object o, final DataElement dataElement) { + if (o == null) { + throw new NullPointerException(dataElement + " cannot be null"); + } + if (!(o instanceof Comparable)) { + throw new ClassCastException(dataElement + " must be Comparable"); + } + } + + /** + * check a key for validity (non-null and implements Comparable) + * + * @param key the key to be checked + * + * @throws NullPointerException if key is null + * @throws ClassCastException if key is not Comparable + */ + private static void checkKey(final Object key) { + checkNonNullComparable(key, DataElement.KEY); + } + + /** + * check a value for validity (non-null and implements Comparable) + * + * @param value the value to be checked + * + * @throws NullPointerException if value is null + * @throws ClassCastException if value is not Comparable + */ + private static void checkValue(final Object value) { + checkNonNullComparable(value, DataElement.VALUE); + } + + /** + * check a key and a value for validity (non-null and implements + * Comparable) + * + * @param key the key to be checked + * @param value the value to be checked + * + * @throws NullPointerException if key or value is null + * @throws ClassCastException if key or value is not Comparable + */ + private static void checkKeyAndValue(final Object key, final Object value) { + checkKey(key); + checkValue(value); + } + + /** + * increment the modification count -- used to check for + * concurrent modification of the map through the map and through + * an Iterator from one of its Set or Collection views + */ + private void modify() { + modifications++; + } + + /** + * bump up the size and note that the map has changed + */ + private void grow() { + modify(); + nodeCount++; + } + + /** + * decrement the size and note that the map has changed + */ + private void shrink() { + modify(); + nodeCount--; + } + + /** + * insert a node by its value + * + * @param newNode the node to be inserted + * + * @throws IllegalArgumentException if the node already exists + * in the value mapping + */ + private void insertValue(final Node newNode) throws IllegalArgumentException { + Node node = rootNode[DataElement.VALUE.ordinal()]; + + while (true) { + final int cmp = compare(newNode.getValue(), node.getValue()); + + if (cmp == 0) { + throw new IllegalArgumentException( + "Cannot store a duplicate value (\"" + newNode.getData(DataElement.VALUE) + "\") in this Map"); + } else if (cmp < 0) { + if (node.getLeft(DataElement.VALUE) != null) { + node = node.getLeft(DataElement.VALUE); + } else { + node.setLeft(newNode, DataElement.VALUE); + newNode.setParent(node, DataElement.VALUE); + doRedBlackInsert(newNode, DataElement.VALUE); + + break; + } + } else { // cmp > 0 + if (node.getRight(DataElement.VALUE) != null) { + node = node.getRight(DataElement.VALUE); + } else { + node.setRight(newNode, DataElement.VALUE); + newNode.setParent(node, DataElement.VALUE); + doRedBlackInsert(newNode, DataElement.VALUE); + + break; + } + } + } + } + + //----------------------------------------------------------------------- + /** + * Compares for equals as per the API. + * + * @param obj the object to compare to + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return true if equal + */ + private boolean doEquals(final Object obj, final DataElement dataElement) { + if (obj == this) { + return true; + } + if (!(obj instanceof Map)) { + return false; + } + final Map other = (Map) obj; + if (other.size() != size()) { + return false; + } + + if (nodeCount > 0) { + try { + for (final MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { + final Object key = it.next(); + final Object value = it.getValue(); + if (!value.equals(other.get(key))) { + return false; + } + } + } catch (final ClassCastException | NullPointerException ex) { + return false; + } + } + return true; + } + + /** + * Gets the hash code value for this map as per the API. + * + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return the hash code value for this map + */ + private int doHashCode(final DataElement dataElement) { + int total = 0; + if (nodeCount > 0) { + for (final MapIterator it = getMapIterator(dataElement); it.hasNext(); ) { + final Object key = it.next(); + final Object value = it.getValue(); + total += key.hashCode() ^ value.hashCode(); + } + } + return total; + } + + /** + * Gets the string form of this map as per AbstractMap. + * + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return the string form of this map + */ + private String doToString(final DataElement dataElement) { + if (nodeCount == 0) { + return "{}"; + } + final StringBuilder buf = new StringBuilder(nodeCount * 32); + buf.append('{'); + final MapIterator it = getMapIterator(dataElement); + boolean hasNext = it.hasNext(); + while (hasNext) { + final Object key = it.next(); + final Object value = it.getValue(); + buf.append(key == this ? "(this Map)" : key) + .append('=') + .append(value == this ? "(this Map)" : value); + + hasNext = it.hasNext(); + if (hasNext) { + buf.append(", "); + } + } + + buf.append('}'); + return buf.toString(); + } + + private MapIterator getMapIterator(final DataElement dataElement) { + switch (dataElement) { + case KEY: + return new ViewMapIterator(DataElement.KEY); + case VALUE: + return new InverseViewMapIterator(DataElement.VALUE); + default: + throw new IllegalArgumentException(); + } + } + + /** + * Reads the content of the stream. + * + * @param stream the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + */ + @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect + private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException{ + stream.defaultReadObject(); + rootNode = new Node[2]; + final int size = stream.readInt(); + for(int i = 0; i < size; i++){ + final K k =(K) stream.readObject(); + final V v =(V) stream.readObject(); + put(k, v); + } + } + + /** + * Writes the content to the stream for serialization. + * + * @param stream the output stream + * @throws IOException if an error occurs while writing to the stream + */ + private void writeObject(final ObjectOutputStream stream) throws IOException{ + stream.defaultWriteObject(); + stream.writeInt(this.size()); + for (final Entry entry : entrySet()) { + stream.writeObject(entry.getKey()); + stream.writeObject(entry.getValue()); + } + } + + //----------------------------------------------------------------------- + /** + * A view of this map. + */ + abstract class View extends AbstractSet { + + /** Whether to return KEY or VALUE order. */ + final DataElement orderType; + + /** + * Constructor. + * @param orderType the KEY or VALUE int for the order + */ + View(final DataElement orderType) { + super(); + this.orderType = orderType; + } + + @Override + public int size() { + return TreeBidiMap.this.size(); + } + + @Override + public void clear() { + TreeBidiMap.this.clear(); + } + } + + class KeyView extends View { + + /** + * Create a new TreeBidiMap.KeyView. + */ + public KeyView(final DataElement orderType) { + super(orderType); + } + + @Override + public Iterator iterator() { + return new ViewMapIterator(orderType); + } + + @Override + public boolean contains(final Object obj) { + checkNonNullComparable(obj, DataElement.KEY); + return lookupKey(obj) != null; + } + + @Override + public boolean remove(final Object o) { + return doRemoveKey(o) != null; + } + + } + + class ValueView extends View { + + /** + * Create a new TreeBidiMap.ValueView. + */ + public ValueView(final DataElement orderType) { + super(orderType); + } + + @Override + public Iterator iterator() { + return new InverseViewMapIterator(orderType); + } + + @Override + public boolean contains(final Object obj) { + checkNonNullComparable(obj, DataElement.VALUE); + return lookupValue(obj) != null; + } + + @Override + public boolean remove(final Object o) { + return doRemoveValue(o) != null; + } + + } + + /** + * A view of this map. + */ + class EntryView extends View> { + + EntryView() { + super(DataElement.KEY); + } + + @Override + public boolean contains(final Object obj) { + if (!(obj instanceof Map.Entry)) { + return false; + } + final Entry entry = (Entry) obj; + final Object value = entry.getValue(); + final Node node = lookupKey(entry.getKey()); + return node != null && node.getValue().equals(value); + } + + @Override + public boolean remove(final Object obj) { + if (!(obj instanceof Map.Entry)) { + return false; + } + final Entry entry = (Entry) obj; + final Object value = entry.getValue(); + final Node node = lookupKey(entry.getKey()); + if (node != null && node.getValue().equals(value)) { + doRedBlackDelete(node); + return true; + } + return false; + } + + @Override + public Iterator> iterator() { + return new ViewMapEntryIterator(); + } + } + + /** + * A view of this map. + */ + class InverseEntryView extends View> { + + InverseEntryView() { + super(DataElement.VALUE); + } + + @Override + public boolean contains(final Object obj) { + if (!(obj instanceof Map.Entry)) { + return false; + } + final Entry entry = (Entry) obj; + final Object value = entry.getValue(); + final Node node = lookupValue(entry.getKey()); + return node != null && node.getKey().equals(value); + } + + @Override + public boolean remove(final Object obj) { + if (!(obj instanceof Map.Entry)) { + return false; + } + final Entry entry = (Entry) obj; + final Object value = entry.getValue(); + final Node node = lookupValue(entry.getKey()); + if (node != null && node.getKey().equals(value)) { + doRedBlackDelete(node); + return true; + } + return false; + } + + @Override + public Iterator> iterator() { + return new InverseViewMapEntryIterator(); + } + } + + //----------------------------------------------------------------------- + /** + * An iterator over the map. + */ + abstract class ViewIterator { + + /** Whether to return KEY or VALUE order. */ + private final DataElement orderType; + /** The last node returned by the iterator. */ + Node lastReturnedNode; + /** The next node to be returned by the iterator. */ + private Node nextNode; + /** The previous node in the sequence returned by the iterator. */ + private Node previousNode; + /** The modification count. */ + private int expectedModifications; + + /** + * Constructor. + * @param orderType the KEY or VALUE int for the order + */ + ViewIterator(final DataElement orderType) { + super(); + this.orderType = orderType; + expectedModifications = modifications; + nextNode = leastNode(rootNode[orderType.ordinal()], orderType); + lastReturnedNode = null; + previousNode = null; + } + + public final boolean hasNext() { + return nextNode != null; + } + + protected Node navigateNext() { + if (nextNode == null) { + throw new NoSuchElementException(); + } + if (modifications != expectedModifications) { + throw new ConcurrentModificationException(); + } + lastReturnedNode = nextNode; + previousNode = nextNode; + nextNode = nextGreater(nextNode, orderType); + return lastReturnedNode; + } + + public boolean hasPrevious() { + return previousNode != null; + } + + protected Node navigatePrevious() { + if (previousNode == null) { + throw new NoSuchElementException(); + } + if (modifications != expectedModifications) { + throw new ConcurrentModificationException(); + } + nextNode = lastReturnedNode; + if (nextNode == null) { + nextNode = nextGreater(previousNode, orderType); + } + lastReturnedNode = previousNode; + previousNode = nextSmaller(previousNode, orderType); + return lastReturnedNode; + } + + public final void remove() { + if (lastReturnedNode == null) { + throw new IllegalStateException(); + } + if (modifications != expectedModifications) { + throw new ConcurrentModificationException(); + } + doRedBlackDelete(lastReturnedNode); + expectedModifications++; + lastReturnedNode = null; + if (nextNode == null) { + previousNode = greatestNode(rootNode[orderType.ordinal()], orderType); + } else { + previousNode = nextSmaller(nextNode, orderType); + } + } + } + + //----------------------------------------------------------------------- + /** + * An iterator over the map. + */ + class ViewMapIterator extends ViewIterator implements OrderedMapIterator { + + /** + * Constructor. + */ + ViewMapIterator(final DataElement orderType) { + super(orderType); + } + + @Override + public K getKey() { + if (lastReturnedNode == null) { + throw new IllegalStateException( + "Iterator getKey() can only be called after next() and before remove()"); + } + return lastReturnedNode.getKey(); + } + + @Override + public V getValue() { + if (lastReturnedNode == null) { + throw new IllegalStateException( + "Iterator getValue() can only be called after next() and before remove()"); + } + return lastReturnedNode.getValue(); + } + + @Override + public V setValue(final V obj) { + throw new UnsupportedOperationException(); + } + + @Override + public K next() { + return navigateNext().getKey(); + } + + @Override + public K previous() { + return navigatePrevious().getKey(); + } + } + + /** + * An iterator over the map. + */ + class InverseViewMapIterator extends ViewIterator implements OrderedMapIterator { + + /** + * Create a new TreeBidiMap.InverseViewMapIterator. + */ + public InverseViewMapIterator(final DataElement orderType) { + super(orderType); + } + + @Override + public V getKey() { + if (lastReturnedNode == null) { + throw new IllegalStateException( + "Iterator getKey() can only be called after next() and before remove()"); + } + return lastReturnedNode.getValue(); + } + + @Override + public K getValue() { + if (lastReturnedNode == null) { + throw new IllegalStateException( + "Iterator getValue() can only be called after next() and before remove()"); + } + return lastReturnedNode.getKey(); + } + + @Override + public K setValue(final K obj) { + throw new UnsupportedOperationException(); + } + + @Override + public V next() { + return navigateNext().getValue(); + } + + @Override + public V previous() { + return navigatePrevious().getValue(); + } + } + + /** + * An iterator over the map entries. + */ + class ViewMapEntryIterator extends ViewIterator implements OrderedIterator> { + + /** + * Constructor. + */ + ViewMapEntryIterator() { + super(DataElement.KEY); + } + + @Override + public Entry next() { + return navigateNext(); + } + + @Override + public Entry previous() { + return navigatePrevious(); + } + } + + /** + * An iterator over the inverse map entries. + */ + class InverseViewMapEntryIterator extends ViewIterator implements OrderedIterator> { + + /** + * Constructor. + */ + InverseViewMapEntryIterator() { + super(DataElement.VALUE); + } + + @Override + public Entry next() { + return createEntry(navigateNext()); + } + + @Override + public Entry previous() { + return createEntry(navigatePrevious()); + } + + private Entry createEntry(final Node node) { + return new UnmodifiableMapEntry<>(node.getValue(), node.getKey()); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + /** + * A node used to store the data. + */ + static class Node, V extends Comparable> implements Entry, KeyValue { + + private final K key; + private final V value; + private final Node[] leftNode; + private final Node[] rightNode; + private final Node[] parentNode; + private final boolean[] blackColor; + private int hashcodeValue; + private boolean calculatedHashCode; + + /** + * Make a new cell with given key and value, and with null + * links, and black (true) colors. + * + * @param key the key of this node + * @param value the value of this node + */ + @SuppressWarnings("unchecked") + Node(final K key, final V value) { + super(); + this.key = key; + this.value = value; + leftNode = new Node[2]; + rightNode = new Node[2]; + parentNode = new Node[2]; + blackColor = new boolean[] { true, true }; + calculatedHashCode = false; + } + + private Object getData(final DataElement dataElement) { + switch (dataElement) { + case KEY: + return getKey(); + case VALUE: + return getValue(); + default: + throw new IllegalArgumentException(); + } + } + + private void setLeft(final Node node, final DataElement dataElement) { + leftNode[dataElement.ordinal()] = node; + } + + private Node getLeft(final DataElement dataElement) { + return leftNode[dataElement.ordinal()]; + } + + private void setRight(final Node node, final DataElement dataElement) { + rightNode[dataElement.ordinal()] = node; + } + + private Node getRight(final DataElement dataElement) { + return rightNode[dataElement.ordinal()]; + } + + /** + * Set this node's parent node. + * + * @param node the new parent node + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private void setParent(final Node node, final DataElement dataElement) { + parentNode[dataElement.ordinal()] = node; + } + + /** + * Get the parent node. + * + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return the parent node, may be null + */ + private Node getParent(final DataElement dataElement) { + return parentNode[dataElement.ordinal()]; + } + + /** + * Exchange colors with another node. + * + * @param node the node to swap with + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private void swapColors(final Node node, final DataElement dataElement) { + // Swap colors -- old hacker's trick + blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; + node.blackColor[dataElement.ordinal()] ^= blackColor[dataElement.ordinal()]; + blackColor[dataElement.ordinal()] ^= node.blackColor[dataElement.ordinal()]; + } + + /** + * Is this node black? + * + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return true if black (which is represented as a true boolean) + */ + private boolean isBlack(final DataElement dataElement) { + return blackColor[dataElement.ordinal()]; + } + + /** + * Is this node red? + * + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + * @return true if non-black + */ + private boolean isRed(final DataElement dataElement) { + return !blackColor[dataElement.ordinal()]; + } + + /** + * Make this node black. + * + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private void setBlack(final DataElement dataElement) { + blackColor[dataElement.ordinal()] = true; + } + + /** + * Make this node red. + * + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private void setRed(final DataElement dataElement) { + blackColor[dataElement.ordinal()] = false; + } + + /** + * Make this node the same color as another + * + * @param node the node whose color we're adopting + * @param dataElement either {@link DataElement#KEY} key} + * or the {@link DataElement#VALUE value}. + */ + private void copyColor(final Node node, final DataElement dataElement) { + blackColor[dataElement.ordinal()] = node.blackColor[dataElement.ordinal()]; + } + + private boolean isLeftChild(final DataElement dataElement) { + return parentNode[dataElement.ordinal()] != null + && parentNode[dataElement.ordinal()].leftNode[dataElement.ordinal()] == this; + } + + private boolean isRightChild(final DataElement dataElement) { + return parentNode[dataElement.ordinal()] != null + && parentNode[dataElement.ordinal()].rightNode[dataElement.ordinal()] == this; + } + + //------------------------------------------------------------------- + /** + * Gets the key. + * + * @return the key corresponding to this entry. + */ + @Override + public K getKey() { + return key; + } + + /** + * Gets the value. + * + * @return the value corresponding to this entry. + */ + @Override + public V getValue() { + return value; + } + + /** + * Optional operation that is not permitted in this implementation + * + * @param ignored this parameter is ignored. + * @return does not return + * @throws UnsupportedOperationException always + */ + @Override + public V setValue(final V ignored) throws UnsupportedOperationException { + throw new UnsupportedOperationException("Map.Entry.setValue is not supported"); + } + + /** + * Compares the specified object with this entry for equality. + * Returns true if the given object is also a map entry and + * the two entries represent the same mapping. + * + * @param obj the object to be compared for equality with this entry. + * @return true if the specified object is equal to this entry. + */ + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Map.Entry)) { + return false; + } + final Entry e = (Entry) obj; + return getKey().equals(e.getKey()) && getValue().equals(e.getValue()); + } + + /** + * @return the hash code value for this map entry. + */ + @Override + public int hashCode() { + if (!calculatedHashCode) { + hashcodeValue = getKey().hashCode() ^ getValue().hashCode(); + calculatedHashCode = true; + } + return hashcodeValue; + } + } + + //----------------------------------------------------------------------- + /** + * The inverse map implementation. + */ + class Inverse implements OrderedBidiMap { + + /** Store the keySet once created. */ + private Set inverseKeySet; + /** Store the valuesSet once created. */ + private Set inverseValuesSet; + /** Store the entrySet once created. */ + private Set> inverseEntrySet; + + @Override + public int size() { + return TreeBidiMap.this.size(); + } + + @Override + public boolean isEmpty() { + return TreeBidiMap.this.isEmpty(); + } + + @Override + public K get(final Object key) { + return TreeBidiMap.this.getKey(key); + } + + @Override + public V getKey(final Object value) { + return TreeBidiMap.this.get(value); + } + + @Override + public boolean containsKey(final Object key) { + return TreeBidiMap.this.containsValue(key); + } + + @Override + public boolean containsValue(final Object value) { + return TreeBidiMap.this.containsKey(value); + } + + @Override + public V firstKey() { + if (TreeBidiMap.this.nodeCount == 0) { + throw new NoSuchElementException("Map is empty"); + } + return leastNode(TreeBidiMap.this.rootNode[DataElement.VALUE.ordinal()], DataElement.VALUE).getValue(); + } + + @Override + public V lastKey() { + if (TreeBidiMap.this.nodeCount == 0) { + throw new NoSuchElementException("Map is empty"); + } + return greatestNode(TreeBidiMap.this.rootNode[DataElement.VALUE.ordinal()], DataElement.VALUE).getValue(); + } + + @Override + public V nextKey(final V key) { + checkKey(key); + final Node node = nextGreater(TreeBidiMap.this.lookup(key, DataElement.VALUE), DataElement.VALUE); + return node == null ? null : node.getValue(); + } + + @Override + public V previousKey(final V key) { + checkKey(key); + final Node node = TreeBidiMap.this.nextSmaller(TreeBidiMap.this.lookup(key, DataElement.VALUE), DataElement.VALUE); + return node == null ? null : node.getValue(); + } + + @Override + public K put(final V key, final K value) { + final K result = get(key); + TreeBidiMap.this.doPut(value, key); + return result; + } + + @Override + public void putAll(final Map map) { + for (final Entry e : map.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + @Override + public K remove(final Object key) { + return TreeBidiMap.this.removeValue(key); + } + + @Override + public V removeValue(final Object value) { + return TreeBidiMap.this.remove(value); + } + + @Override + public void clear() { + TreeBidiMap.this.clear(); + } + + @Override + public Set keySet() { + if (inverseKeySet == null) { + inverseKeySet = new ValueView(DataElement.VALUE); + } + return inverseKeySet; + } + + @Override + public Set values() { + if (inverseValuesSet == null) { + inverseValuesSet = new KeyView(DataElement.VALUE); + } + return inverseValuesSet; + } + + @Override + public Set> entrySet() { + if (inverseEntrySet == null) { + inverseEntrySet = new InverseEntryView(); + } + return inverseEntrySet; + } + + @Override + public OrderedMapIterator mapIterator() { + if (isEmpty()) { + return EmptyOrderedMapIterator.emptyOrderedMapIterator(); + } + return new InverseViewMapIterator(DataElement.VALUE); + } + + @Override + public OrderedBidiMap inverseBidiMap() { + return TreeBidiMap.this; + } + + @Override + public boolean equals(final Object obj) { + return TreeBidiMap.this.doEquals(obj, DataElement.VALUE); + } + + @Override + public int hashCode() { + return TreeBidiMap.this.doHashCode(DataElement.VALUE); + } + + @Override + public String toString() { + return TreeBidiMap.this.doToString(DataElement.VALUE); + } + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/Unmodifiable.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/Unmodifiable.java index 7fa6cf6aad..ff3fb5fc37 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/Unmodifiable.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/Unmodifiable.java @@ -1,38 +1,38 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Marker interface for collections, maps and iterators that are unmodifiable. - *

- * This interface enables testing such as: - *

- *
- * if (coll instanceof Unmodifiable) {
- *   coll = new ArrayList(coll);
- * }
- * // now we know coll is modifiable
- * 
- * Of course all this only works if you use the Unmodifiable classes defined - * in this library. If you use the JDK unmodifiable class via {@code java.util Collections} - * then the interface won't be there. - * - * @since 3.0 - */ -public interface Unmodifiable { - // marker interface - no methods to implement -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Marker interface for collections, maps and iterators that are unmodifiable. + *

+ * This interface enables testing such as: + *

+ *
+ * if (coll instanceof Unmodifiable) {
+ *   coll = new ArrayList(coll);
+ * }
+ * // now we know coll is modifiable
+ * 
+ * Of course all this only works if you use the Unmodifiable classes defined + * in this library. If you use the JDK unmodifiable class via {@code java.util Collections} + * then the interface won't be there. + * + * @since 3.0 + */ +public interface Unmodifiable { + // marker interface - no methods to implement +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableCollection.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableCollection.java index 251937dd4a..2d7cceea46 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableCollection.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableCollection.java @@ -1,118 +1,118 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Collection; -import java.util.Iterator; -import java.util.function.Predicate; - -/** - * Decorates another {@link Collection} to ensure it can't be altered. - *

- * This class is Serializable from Commons Collections 3.1. - *

- *

- * Attempts to modify it will result in an UnsupportedOperationException. - *

- * - * @param the type of the elements in the collection - * @since 3.0 - */ -public final class UnmodifiableCollection - extends AbstractCollectionDecorator - implements Unmodifiable { - - /** Serialization version */ - private static final long serialVersionUID = -239892006883819945L; - - /** - * Factory method to create an unmodifiable collection. - *

- * If the collection passed in is already unmodifiable, it is returned. - * - * @param the type of the elements in the collection - * @param coll the collection to decorate, must not be null - * @return an unmodifiable collection - * @throws NullPointerException if collection is null - * @since 4.0 - */ - public static Collection unmodifiableCollection(final Collection coll) { - if (coll instanceof Unmodifiable) { - @SuppressWarnings("unchecked") // safe to upcast - final Collection tmpColl = (Collection) coll; - return tmpColl; - } - return new UnmodifiableCollection<>(coll); - } - - //----------------------------------------------------------------------- - /** - * Constructor that wraps (not copies). - * - * @param coll the collection to decorate, must not be null - * @throws NullPointerException if collection is null - */ - @SuppressWarnings("unchecked") // safe to upcast - private UnmodifiableCollection(final Collection coll) { - super((Collection) coll); - } - - //----------------------------------------------------------------------- - @Override - public Iterator iterator() { - return UnmodifiableIterator.unmodifiableIterator(decorated().iterator()); - } - - @Override - public boolean add(final E object) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(final Object object) { - throw new UnsupportedOperationException(); - } - - /** - * @since 4.4 - */ - @Override - public boolean removeIf(final Predicate filter) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean retainAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Predicate; + +/** + * Decorates another {@link Collection} to ensure it can't be altered. + *

+ * This class is Serializable from Commons Collections 3.1. + *

+ *

+ * Attempts to modify it will result in an UnsupportedOperationException. + *

+ * + * @param the type of the elements in the collection + * @since 3.0 + */ +public final class UnmodifiableCollection + extends AbstractCollectionDecorator + implements Unmodifiable { + + /** Serialization version */ + private static final long serialVersionUID = -239892006883819945L; + + /** + * Factory method to create an unmodifiable collection. + *

+ * If the collection passed in is already unmodifiable, it is returned. + * + * @param the type of the elements in the collection + * @param coll the collection to decorate, must not be null + * @return an unmodifiable collection + * @throws NullPointerException if collection is null + * @since 4.0 + */ + public static Collection unmodifiableCollection(final Collection coll) { + if (coll instanceof Unmodifiable) { + @SuppressWarnings("unchecked") // safe to upcast + final Collection tmpColl = (Collection) coll; + return tmpColl; + } + return new UnmodifiableCollection<>(coll); + } + + //----------------------------------------------------------------------- + /** + * Constructor that wraps (not copies). + * + * @param coll the collection to decorate, must not be null + * @throws NullPointerException if collection is null + */ + @SuppressWarnings("unchecked") // safe to upcast + private UnmodifiableCollection(final Collection coll) { + super((Collection) coll); + } + + //----------------------------------------------------------------------- + @Override + public Iterator iterator() { + return UnmodifiableIterator.unmodifiableIterator(decorated().iterator()); + } + + @Override + public boolean add(final E object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(final Object object) { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.4 + */ + @Override + public boolean removeIf(final Predicate filter) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableEntrySet.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableEntrySet.java index 2954d94dc6..21611558aa 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableEntrySet.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableEntrySet.java @@ -1,190 +1,190 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.lang.reflect.Array; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; - -/** - * Decorates a map entry Set to ensure it can't be altered. - *

- * Attempts to modify it will result in an UnsupportedOperationException. - *

- * - * @param the type of the keys in the map - * @param the type of the values in the map - * - * @since 3.0 - */ -public final class UnmodifiableEntrySet - extends AbstractSetDecorator> implements Unmodifiable { - - /** Serialization version */ - private static final long serialVersionUID = 1678353579659253473L; - - /** - * Factory method to create an unmodifiable set of Map Entry objects. - * - * @param the key type - * @param the value type - * @param set the set to decorate, must not be null - * @return a new unmodifiable entry set - * @throws NullPointerException if set is null - * @since 4.0 - */ - public static Set> unmodifiableEntrySet(final Set> set) { - if (set instanceof Unmodifiable) { - return set; - } - return new UnmodifiableEntrySet<>(set); - } - - //----------------------------------------------------------------------- - /** - * Constructor that wraps (not copies). - * - * @param set the set to decorate, must not be null - * @throws NullPointerException if set is null - */ - private UnmodifiableEntrySet(final Set> set) { - super(set); - } - - //----------------------------------------------------------------------- - @Override - public boolean add(final Map.Entry object) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(final Collection> coll) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(final Object object) { - throw new UnsupportedOperationException(); - } - - /** - * @since 4.4 - */ - @Override - public boolean removeIf(Predicate> filter) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean retainAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - //----------------------------------------------------------------------- - @Override - public Iterator> iterator() { - return new UnmodifiableEntrySetIterator(decorated().iterator()); - } - - @Override - @SuppressWarnings("unchecked") - public Object[] toArray() { - final Object[] array = decorated().toArray(); - for (int i = 0; i < array.length; i++) { - array[i] = new UnmodifiableEntry((Map.Entry) array[i]); - } - return array; - } - - @Override - @SuppressWarnings("unchecked") - public T[] toArray(final T[] array) { - Object[] result = array; - if (array.length > 0) { - // we must create a new array to handle multi-threaded situations - // where another thread could access data before we decorate it - result = (Object[]) Array.newInstance(array.getClass().getComponentType(), 0); - } - result = decorated().toArray(result); - for (int i = 0; i < result.length; i++) { - result[i] = new UnmodifiableEntry((Map.Entry) result[i]); - } - - // check to see if result should be returned straight - if (result.length > array.length) { - return (T[]) result; - } - - // copy back into input array to fulfill the method contract - System.arraycopy(result, 0, array, 0, result.length); - if (array.length > result.length) { - array[result.length] = null; - } - return array; - } - - //----------------------------------------------------------------------- - /** - * Implementation of an entry set iterator. - */ - private class UnmodifiableEntrySetIterator extends AbstractIteratorDecorator> { - - protected UnmodifiableEntrySetIterator(final Iterator> iterator) { - super(iterator); - } - - @Override - public Map.Entry next() { - return new UnmodifiableEntry(getIterator().next()); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } - - //----------------------------------------------------------------------- - /** - * Implementation of a map entry that is unmodifiable. - */ - private class UnmodifiableEntry extends AbstractMapEntryDecorator { - - protected UnmodifiableEntry(final Map.Entry entry) { - super(entry); - } - - @Override - public V setValue(final V obj) { - throw new UnsupportedOperationException(); - } - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +/** + * Decorates a map entry Set to ensure it can't be altered. + *

+ * Attempts to modify it will result in an UnsupportedOperationException. + *

+ * + * @param the type of the keys in the map + * @param the type of the values in the map + * + * @since 3.0 + */ +public final class UnmodifiableEntrySet + extends AbstractSetDecorator> implements Unmodifiable { + + /** Serialization version */ + private static final long serialVersionUID = 1678353579659253473L; + + /** + * Factory method to create an unmodifiable set of Map Entry objects. + * + * @param the key type + * @param the value type + * @param set the set to decorate, must not be null + * @return a new unmodifiable entry set + * @throws NullPointerException if set is null + * @since 4.0 + */ + public static Set> unmodifiableEntrySet(final Set> set) { + if (set instanceof Unmodifiable) { + return set; + } + return new UnmodifiableEntrySet<>(set); + } + + //----------------------------------------------------------------------- + /** + * Constructor that wraps (not copies). + * + * @param set the set to decorate, must not be null + * @throws NullPointerException if set is null + */ + private UnmodifiableEntrySet(final Set> set) { + super(set); + } + + //----------------------------------------------------------------------- + @Override + public boolean add(final Map.Entry object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(final Collection> coll) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(final Object object) { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.4 + */ + @Override + public boolean removeIf(Predicate> filter) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + //----------------------------------------------------------------------- + @Override + public Iterator> iterator() { + return new UnmodifiableEntrySetIterator(decorated().iterator()); + } + + @Override + @SuppressWarnings("unchecked") + public Object[] toArray() { + final Object[] array = decorated().toArray(); + for (int i = 0; i < array.length; i++) { + array[i] = new UnmodifiableEntry((Map.Entry) array[i]); + } + return array; + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(final T[] array) { + Object[] result = array; + if (array.length > 0) { + // we must create a new array to handle multi-threaded situations + // where another thread could access data before we decorate it + result = (Object[]) Array.newInstance(array.getClass().getComponentType(), 0); + } + result = decorated().toArray(result); + for (int i = 0; i < result.length; i++) { + result[i] = new UnmodifiableEntry((Map.Entry) result[i]); + } + + // check to see if result should be returned straight + if (result.length > array.length) { + return (T[]) result; + } + + // copy back into input array to fulfill the method contract + System.arraycopy(result, 0, array, 0, result.length); + if (array.length > result.length) { + array[result.length] = null; + } + return array; + } + + //----------------------------------------------------------------------- + /** + * Implementation of an entry set iterator. + */ + private class UnmodifiableEntrySetIterator extends AbstractIteratorDecorator> { + + protected UnmodifiableEntrySetIterator(final Iterator> iterator) { + super(iterator); + } + + @Override + public Map.Entry next() { + return new UnmodifiableEntry(getIterator().next()); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + //----------------------------------------------------------------------- + /** + * Implementation of a map entry that is unmodifiable. + */ + private class UnmodifiableEntry extends AbstractMapEntryDecorator { + + protected UnmodifiableEntry(final Map.Entry entry) { + super(entry); + } + + @Override + public V setValue(final V obj) { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableIterator.java index 486c547cc7..ed80d2586a 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableIterator.java @@ -1,84 +1,84 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Iterator; - -/** - * Decorates an iterator such that it cannot be modified. - *

- * Attempts to modify it will result in an UnsupportedOperationException. - *

- * - * @since 3.0 - */ -public final class UnmodifiableIterator implements Iterator, Unmodifiable { - - /** The iterator being decorated */ - private final Iterator iterator; - - //----------------------------------------------------------------------- - /** - * Decorates the specified iterator such that it cannot be modified. - *

- * If the iterator is already unmodifiable it is returned directly. - * - * @param the element type - * @param iterator the iterator to decorate - * @return a new unmodifiable iterator - * @throws NullPointerException if the iterator is null - */ - public static Iterator unmodifiableIterator(final Iterator iterator) { - if (iterator == null) { - throw new NullPointerException("Iterator must not be null"); - } - if (iterator instanceof Unmodifiable) { - @SuppressWarnings("unchecked") // safe to upcast - final Iterator tmpIterator = (Iterator) iterator; - return tmpIterator; - } - return new UnmodifiableIterator<>(iterator); - } - - //----------------------------------------------------------------------- - /** - * Constructor. - * - * @param iterator the iterator to decorate - */ - private UnmodifiableIterator(final Iterator iterator) { - super(); - this.iterator = iterator; - } - - //----------------------------------------------------------------------- - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public E next() { - return iterator.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("remove() is not supported"); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Iterator; + +/** + * Decorates an iterator such that it cannot be modified. + *

+ * Attempts to modify it will result in an UnsupportedOperationException. + *

+ * + * @since 3.0 + */ +public final class UnmodifiableIterator implements Iterator, Unmodifiable { + + /** The iterator being decorated */ + private final Iterator iterator; + + //----------------------------------------------------------------------- + /** + * Decorates the specified iterator such that it cannot be modified. + *

+ * If the iterator is already unmodifiable it is returned directly. + * + * @param the element type + * @param iterator the iterator to decorate + * @return a new unmodifiable iterator + * @throws NullPointerException if the iterator is null + */ + public static Iterator unmodifiableIterator(final Iterator iterator) { + if (iterator == null) { + throw new NullPointerException("Iterator must not be null"); + } + if (iterator instanceof Unmodifiable) { + @SuppressWarnings("unchecked") // safe to upcast + final Iterator tmpIterator = (Iterator) iterator; + return tmpIterator; + } + return new UnmodifiableIterator<>(iterator); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param iterator the iterator to decorate + */ + private UnmodifiableIterator(final Iterator iterator) { + super(); + this.iterator = iterator; + } + + //----------------------------------------------------------------------- + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public E next() { + return iterator.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove() is not supported"); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableList.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableList.java index a55930b44f..57291bd376 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableList.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableList.java @@ -1,154 +1,154 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.function.Predicate; - -/** - * Decorates another List to ensure it can't be altered. - *

- * This class is Serializable from Commons Collections 3.1. - *

- *

- * Attempts to modify it will result in an UnsupportedOperationException. - *

- * - * @since 3.0 - */ -public final class UnmodifiableList - extends AbstractSerializableListDecorator - implements Unmodifiable { - - /** Serialization version */ - private static final long serialVersionUID = 6595182819922443652L; - - /** - * Factory method to create an unmodifiable list. - * - * @param the type of the elements in the list - * @param list the list to decorate, must not be null - * @return a new unmodifiable list - * @throws NullPointerException if list is null - * @since 4.0 - */ - public static List unmodifiableList(final List list) { - if (list instanceof Unmodifiable) { - @SuppressWarnings("unchecked") // safe to upcast - final List tmpList = (List) list; - return tmpList; - } - return new UnmodifiableList<>(list); - } - - //----------------------------------------------------------------------- - /** - * Constructor that wraps (not copies). - * - * @param list the list to decorate, must not be null - * @throws NullPointerException if list is null - */ - @SuppressWarnings("unchecked") // safe to upcast - public UnmodifiableList(final List list) { - super((List) list); - } - - //----------------------------------------------------------------------- - @Override - public Iterator iterator() { - return UnmodifiableIterator.unmodifiableIterator(decorated().iterator()); - } - - @Override - public boolean add(final Object object) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(final Object object) { - throw new UnsupportedOperationException(); - } - - /** - * @since 4.4 - */ - @Override - public boolean removeIf(Predicate filter) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean retainAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - //----------------------------------------------------------------------- - @Override - public ListIterator listIterator() { - return UnmodifiableListIterator.umodifiableListIterator(decorated().listIterator()); - } - - @Override - public ListIterator listIterator(final int index) { - return UnmodifiableListIterator.umodifiableListIterator(decorated().listIterator(index)); - } - - @Override - public void add(final int index, final E object) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(final int index, final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public E remove(final int index) { - throw new UnsupportedOperationException(); - } - - @Override - public E set(final int index, final E object) { - throw new UnsupportedOperationException(); - } - - @Override - public List subList(final int fromIndex, final int toIndex) { - final List sub = decorated().subList(fromIndex, toIndex); - return new UnmodifiableList<>(sub); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.function.Predicate; + +/** + * Decorates another List to ensure it can't be altered. + *

+ * This class is Serializable from Commons Collections 3.1. + *

+ *

+ * Attempts to modify it will result in an UnsupportedOperationException. + *

+ * + * @since 3.0 + */ +public final class UnmodifiableList + extends AbstractSerializableListDecorator + implements Unmodifiable { + + /** Serialization version */ + private static final long serialVersionUID = 6595182819922443652L; + + /** + * Factory method to create an unmodifiable list. + * + * @param the type of the elements in the list + * @param list the list to decorate, must not be null + * @return a new unmodifiable list + * @throws NullPointerException if list is null + * @since 4.0 + */ + public static List unmodifiableList(final List list) { + if (list instanceof Unmodifiable) { + @SuppressWarnings("unchecked") // safe to upcast + final List tmpList = (List) list; + return tmpList; + } + return new UnmodifiableList<>(list); + } + + //----------------------------------------------------------------------- + /** + * Constructor that wraps (not copies). + * + * @param list the list to decorate, must not be null + * @throws NullPointerException if list is null + */ + @SuppressWarnings("unchecked") // safe to upcast + public UnmodifiableList(final List list) { + super((List) list); + } + + //----------------------------------------------------------------------- + @Override + public Iterator iterator() { + return UnmodifiableIterator.unmodifiableIterator(decorated().iterator()); + } + + @Override + public boolean add(final Object object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(final Object object) { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.4 + */ + @Override + public boolean removeIf(Predicate filter) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + //----------------------------------------------------------------------- + @Override + public ListIterator listIterator() { + return UnmodifiableListIterator.umodifiableListIterator(decorated().listIterator()); + } + + @Override + public ListIterator listIterator(final int index) { + return UnmodifiableListIterator.umodifiableListIterator(decorated().listIterator(index)); + } + + @Override + public void add(final int index, final E object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(final int index, final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public E remove(final int index) { + throw new UnsupportedOperationException(); + } + + @Override + public E set(final int index, final E object) { + throw new UnsupportedOperationException(); + } + + @Override + public List subList(final int fromIndex, final int toIndex) { + final List sub = decorated().subList(fromIndex, toIndex); + return new UnmodifiableList<>(sub); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableListIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableListIterator.java index 605d78e10a..0c313e1c1a 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableListIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableListIterator.java @@ -1,112 +1,112 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.ListIterator; - -/** - * Decorates a list iterator such that it cannot be modified. - *

- * Attempts to modify it will result in an UnsupportedOperationException. - *

- * - * @since 3.0 - */ -public final class UnmodifiableListIterator implements ListIterator, Unmodifiable { - - /** The iterator being decorated */ - private final ListIterator iterator; - - //----------------------------------------------------------------------- - /** - * Decorates the specified iterator such that it cannot be modified. - * - * @param the element type - * @param iterator the iterator to decorate - * @return a new unmodifiable list iterator - * @throws NullPointerException if the iterator is null - */ - public static ListIterator umodifiableListIterator(final ListIterator iterator) { - if (iterator == null) { - throw new NullPointerException("ListIterator must not be null"); - } - if (iterator instanceof Unmodifiable) { - @SuppressWarnings("unchecked") // safe to upcast - final ListIterator tmpIterator = (ListIterator) iterator; - return tmpIterator; - } - return new UnmodifiableListIterator<>(iterator); - } - - //----------------------------------------------------------------------- - /** - * Constructor. - * - * @param iterator the iterator to decorate - */ - private UnmodifiableListIterator(final ListIterator iterator) { - super(); - this.iterator = iterator; - } - - //----------------------------------------------------------------------- - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public E next() { - return iterator.next(); - } - - @Override - public int nextIndex() { - return iterator.nextIndex(); - } - - @Override - public boolean hasPrevious() { - return iterator.hasPrevious(); - } - - @Override - public E previous() { - return iterator.previous(); - } - - @Override - public int previousIndex() { - return iterator.previousIndex(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("remove() is not supported"); - } - - @Override - public void set(final E obj) { - throw new UnsupportedOperationException("set() is not supported"); - } - - @Override - public void add(final E obj) { - throw new UnsupportedOperationException("add() is not supported"); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.ListIterator; + +/** + * Decorates a list iterator such that it cannot be modified. + *

+ * Attempts to modify it will result in an UnsupportedOperationException. + *

+ * + * @since 3.0 + */ +public final class UnmodifiableListIterator implements ListIterator, Unmodifiable { + + /** The iterator being decorated */ + private final ListIterator iterator; + + //----------------------------------------------------------------------- + /** + * Decorates the specified iterator such that it cannot be modified. + * + * @param the element type + * @param iterator the iterator to decorate + * @return a new unmodifiable list iterator + * @throws NullPointerException if the iterator is null + */ + public static ListIterator umodifiableListIterator(final ListIterator iterator) { + if (iterator == null) { + throw new NullPointerException("ListIterator must not be null"); + } + if (iterator instanceof Unmodifiable) { + @SuppressWarnings("unchecked") // safe to upcast + final ListIterator tmpIterator = (ListIterator) iterator; + return tmpIterator; + } + return new UnmodifiableListIterator<>(iterator); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param iterator the iterator to decorate + */ + private UnmodifiableListIterator(final ListIterator iterator) { + super(); + this.iterator = iterator; + } + + //----------------------------------------------------------------------- + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public E next() { + return iterator.next(); + } + + @Override + public int nextIndex() { + return iterator.nextIndex(); + } + + @Override + public boolean hasPrevious() { + return iterator.hasPrevious(); + } + + @Override + public E previous() { + return iterator.previous(); + } + + @Override + public int previousIndex() { + return iterator.previousIndex(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove() is not supported"); + } + + @Override + public void set(final E obj) { + throw new UnsupportedOperationException("set() is not supported"); + } + + @Override + public void add(final E obj) { + throw new UnsupportedOperationException("add() is not supported"); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableMapEntry.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableMapEntry.java index e6e509524d..5a2dd88c13 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableMapEntry.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableMapEntry.java @@ -1,73 +1,73 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Map; - -/** - * A {@link Map.Entry Map.Entry} that throws - * UnsupportedOperationException when setValue is called. - * - * @param the type of keys - * @param the type of mapped values - * @since 3.0 - */ -public final class UnmodifiableMapEntry extends AbstractMapEntry implements Unmodifiable { - - /** - * Constructs a new entry with the specified key and given value. - * - * @param key the key for the entry, may be null - * @param value the value for the entry, may be null - */ - public UnmodifiableMapEntry(final K key, final V value) { - super(key, value); - } - - /** - * Constructs a new entry from the specified KeyValue. - * - * @param pair the pair to copy, must not be null - * @throws NullPointerException if the entry is null - */ - public UnmodifiableMapEntry(final KeyValue pair) { - super(pair.getKey(), pair.getValue()); - } - - /** - * Constructs a new entry from the specified Map.Entry. - * - * @param entry the entry to copy, must not be null - * @throws NullPointerException if the entry is null - */ - public UnmodifiableMapEntry(final Map.Entry entry) { - super(entry.getKey(), entry.getValue()); - } - - /** - * Throws UnsupportedOperationException. - * - * @param value the new value - * @return the previous value - * @throws UnsupportedOperationException always - */ - @Override - public V setValue(final V value) { - throw new UnsupportedOperationException("setValue() is not supported"); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Map; + +/** + * A {@link Map.Entry Map.Entry} that throws + * UnsupportedOperationException when setValue is called. + * + * @param the type of keys + * @param the type of mapped values + * @since 3.0 + */ +public final class UnmodifiableMapEntry extends AbstractMapEntry implements Unmodifiable { + + /** + * Constructs a new entry with the specified key and given value. + * + * @param key the key for the entry, may be null + * @param value the value for the entry, may be null + */ + public UnmodifiableMapEntry(final K key, final V value) { + super(key, value); + } + + /** + * Constructs a new entry from the specified KeyValue. + * + * @param pair the pair to copy, must not be null + * @throws NullPointerException if the entry is null + */ + public UnmodifiableMapEntry(final KeyValue pair) { + super(pair.getKey(), pair.getValue()); + } + + /** + * Constructs a new entry from the specified Map.Entry. + * + * @param entry the entry to copy, must not be null + * @throws NullPointerException if the entry is null + */ + public UnmodifiableMapEntry(final Map.Entry entry) { + super(entry.getKey(), entry.getValue()); + } + + /** + * Throws UnsupportedOperationException. + * + * @param value the new value + * @return the previous value + * @throws UnsupportedOperationException always + */ + @Override + public V setValue(final V value) { + throw new UnsupportedOperationException("setValue() is not supported"); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableOrderedMap.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableOrderedMap.java index 90e3423f66..e1160f7be9 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableOrderedMap.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableOrderedMap.java @@ -1,149 +1,149 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -/** - * Decorates another OrderedMap to ensure it can't be altered. - *

- * This class is Serializable from Commons Collections 3.1. - *

- *

- * Attempts to modify it will result in an UnsupportedOperationException. - *

- * - * @param the type of the keys in this map - * @param the type of the values in this map - * @since 3.0 - */ -public final class UnmodifiableOrderedMap extends AbstractOrderedMapDecorator implements - Unmodifiable, Serializable { - - /** Serialization version */ - private static final long serialVersionUID = 8136428161720526266L; - - /** - * Factory method to create an unmodifiable sorted map. - * - * @param the key type - * @param the value type - * @param map the map to decorate, must not be null - * @return a new ordered map - * @throws NullPointerException if map is null - * @since 4.0 - */ - public static OrderedMap unmodifiableOrderedMap(final OrderedMap map) { - if (map instanceof Unmodifiable) { - @SuppressWarnings("unchecked") // safe to upcast - final OrderedMap tmpMap = (OrderedMap) map; - return tmpMap; - } - return new UnmodifiableOrderedMap<>(map); - } - - //----------------------------------------------------------------------- - /** - * Constructor that wraps (not copies). - * - * @param map the map to decorate, must not be null - * @throws NullPointerException if map is null - */ - @SuppressWarnings("unchecked") // safe to upcast - private UnmodifiableOrderedMap(final OrderedMap map) { - super((OrderedMap) map); - } - - //----------------------------------------------------------------------- - /** - * Write the map out using a custom routine. - * - * @param out the output stream - * @throws IOException if an error occurs while writing to the stream - * @since 3.1 - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeObject(map); - } - - /** - * Read the map in using a custom routine. - * - * @param in the input stream - * @throws IOException if an error occurs while reading from the stream - * @throws ClassNotFoundException if an object read from the stream can not be loaded - * @since 3.1 - */ - @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - map = (Map) in.readObject(); // (1) - } - - //----------------------------------------------------------------------- - @Override - public OrderedMapIterator mapIterator() { - final OrderedMapIterator it = decorated().mapIterator(); - return UnmodifiableOrderedMapIterator.unmodifiableOrderedMapIterator(it); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public V put(final K key, final V value) { - throw new UnsupportedOperationException(); - } - - @Override - public void putAll(final Map mapToCopy) { - throw new UnsupportedOperationException(); - } - - @Override - public V remove(final Object key) { - throw new UnsupportedOperationException(); - } - - @Override - public Set> entrySet() { - final Set> set = super.entrySet(); - return UnmodifiableEntrySet.unmodifiableEntrySet(set); - } - - @Override - public Set keySet() { - final Set set = super.keySet(); - return UnmodifiableSet.unmodifiableSet(set); - } - - @Override - public Collection values() { - final Collection coll = super.values(); - return UnmodifiableCollection.unmodifiableCollection(coll); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Decorates another OrderedMap to ensure it can't be altered. + *

+ * This class is Serializable from Commons Collections 3.1. + *

+ *

+ * Attempts to modify it will result in an UnsupportedOperationException. + *

+ * + * @param the type of the keys in this map + * @param the type of the values in this map + * @since 3.0 + */ +public final class UnmodifiableOrderedMap extends AbstractOrderedMapDecorator implements + Unmodifiable, Serializable { + + /** Serialization version */ + private static final long serialVersionUID = 8136428161720526266L; + + /** + * Factory method to create an unmodifiable sorted map. + * + * @param the key type + * @param the value type + * @param map the map to decorate, must not be null + * @return a new ordered map + * @throws NullPointerException if map is null + * @since 4.0 + */ + public static OrderedMap unmodifiableOrderedMap(final OrderedMap map) { + if (map instanceof Unmodifiable) { + @SuppressWarnings("unchecked") // safe to upcast + final OrderedMap tmpMap = (OrderedMap) map; + return tmpMap; + } + return new UnmodifiableOrderedMap<>(map); + } + + //----------------------------------------------------------------------- + /** + * Constructor that wraps (not copies). + * + * @param map the map to decorate, must not be null + * @throws NullPointerException if map is null + */ + @SuppressWarnings("unchecked") // safe to upcast + private UnmodifiableOrderedMap(final OrderedMap map) { + super((OrderedMap) map); + } + + //----------------------------------------------------------------------- + /** + * Write the map out using a custom routine. + * + * @param out the output stream + * @throws IOException if an error occurs while writing to the stream + * @since 3.1 + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeObject(map); + } + + /** + * Read the map in using a custom routine. + * + * @param in the input stream + * @throws IOException if an error occurs while reading from the stream + * @throws ClassNotFoundException if an object read from the stream can not be loaded + * @since 3.1 + */ + @SuppressWarnings("unchecked") // (1) should only fail if input stream is incorrect + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + map = (Map) in.readObject(); // (1) + } + + //----------------------------------------------------------------------- + @Override + public OrderedMapIterator mapIterator() { + final OrderedMapIterator it = decorated().mapIterator(); + return UnmodifiableOrderedMapIterator.unmodifiableOrderedMapIterator(it); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public V put(final K key, final V value) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(final Map mapToCopy) { + throw new UnsupportedOperationException(); + } + + @Override + public V remove(final Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public Set> entrySet() { + final Set> set = super.entrySet(); + return UnmodifiableEntrySet.unmodifiableEntrySet(set); + } + + @Override + public Set keySet() { + final Set set = super.keySet(); + return UnmodifiableSet.unmodifiableSet(set); + } + + @Override + public Collection values() { + final Collection coll = super.values(); + return UnmodifiableCollection.unmodifiableCollection(coll); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableOrderedMapIterator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableOrderedMapIterator.java index ffa978f5f1..e99671702c 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableOrderedMapIterator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableOrderedMapIterator.java @@ -1,111 +1,111 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -/** - * Decorates an ordered map iterator such that it cannot be modified. - *

- * Attempts to modify it will result in an UnsupportedOperationException. - *

- * - * @param the type of keys - * @param the type of mapped values - * @since 3.0 - */ -public final class UnmodifiableOrderedMapIterator implements OrderedMapIterator, - Unmodifiable { - - /** The iterator being decorated */ - private final OrderedMapIterator iterator; - - //----------------------------------------------------------------------- - /** - * Decorates the specified iterator such that it cannot be modified. - * - * @param the key type - * @param the value type - * @param iterator the iterator to decorate - * @return a new unmodifiable ordered map iterator - * @throws NullPointerException if the iterator is null - */ - public static OrderedMapIterator unmodifiableOrderedMapIterator( - final OrderedMapIterator iterator) { - - if (iterator == null) { - throw new NullPointerException("OrderedMapIterator must not be null"); - } - if (iterator instanceof Unmodifiable) { - @SuppressWarnings("unchecked") // safe to upcast - final OrderedMapIterator tmpIterator = (OrderedMapIterator) iterator; - return tmpIterator; - } - return new UnmodifiableOrderedMapIterator<>(iterator); - } - - //----------------------------------------------------------------------- - /** - * Constructor. - * - * @param iterator the iterator to decorate - */ - private UnmodifiableOrderedMapIterator(final OrderedMapIterator iterator) { - super(); - this.iterator = iterator; - } - - //----------------------------------------------------------------------- - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - - @Override - public K next() { - return iterator.next(); - } - - @Override - public boolean hasPrevious() { - return iterator.hasPrevious(); - } - - @Override - public K previous() { - return iterator.previous(); - } - - @Override - public K getKey() { - return iterator.getKey(); - } - - @Override - public V getValue() { - return iterator.getValue(); - } - - @Override - public V setValue(final V value) { - throw new UnsupportedOperationException("setValue() is not supported"); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("remove() is not supported"); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +/** + * Decorates an ordered map iterator such that it cannot be modified. + *

+ * Attempts to modify it will result in an UnsupportedOperationException. + *

+ * + * @param the type of keys + * @param the type of mapped values + * @since 3.0 + */ +public final class UnmodifiableOrderedMapIterator implements OrderedMapIterator, + Unmodifiable { + + /** The iterator being decorated */ + private final OrderedMapIterator iterator; + + //----------------------------------------------------------------------- + /** + * Decorates the specified iterator such that it cannot be modified. + * + * @param the key type + * @param the value type + * @param iterator the iterator to decorate + * @return a new unmodifiable ordered map iterator + * @throws NullPointerException if the iterator is null + */ + public static OrderedMapIterator unmodifiableOrderedMapIterator( + final OrderedMapIterator iterator) { + + if (iterator == null) { + throw new NullPointerException("OrderedMapIterator must not be null"); + } + if (iterator instanceof Unmodifiable) { + @SuppressWarnings("unchecked") // safe to upcast + final OrderedMapIterator tmpIterator = (OrderedMapIterator) iterator; + return tmpIterator; + } + return new UnmodifiableOrderedMapIterator<>(iterator); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param iterator the iterator to decorate + */ + private UnmodifiableOrderedMapIterator(final OrderedMapIterator iterator) { + super(); + this.iterator = iterator; + } + + //----------------------------------------------------------------------- + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public K next() { + return iterator.next(); + } + + @Override + public boolean hasPrevious() { + return iterator.hasPrevious(); + } + + @Override + public K previous() { + return iterator.previous(); + } + + @Override + public K getKey() { + return iterator.getKey(); + } + + @Override + public V getValue() { + return iterator.getValue(); + } + + @Override + public V setValue(final V value) { + throw new UnsupportedOperationException("setValue() is not supported"); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove() is not supported"); + } + +} diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableSet.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableSet.java index 2a0d47597a..250485d5a7 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableSet.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/collections/UnmodifiableSet.java @@ -1,117 +1,117 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.openjpa.lib.util.collections; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; -import java.util.function.Predicate; - -/** - * Decorates another Set to ensure it can't be altered. - *

- * This class is Serializable from Commons Collections 3.1. - *

- *

- * Attempts to modify it will result in an UnsupportedOperationException. - *

- * - * @param the type of the elements in this set - * @since 3.0 - */ -public final class UnmodifiableSet - extends AbstractSerializableSetDecorator - implements Unmodifiable { - - /** Serialization version */ - private static final long serialVersionUID = 6499119872185240161L; - - /** - * Factory method to create an unmodifiable set. - * - * @param the element type - * @param set the set to decorate, must not be null - * @return a new unmodifiable set - * @throws NullPointerException if set is null - * @since 4.0 - */ - public static Set unmodifiableSet(final Set set) { - if (set instanceof Unmodifiable) { - @SuppressWarnings("unchecked") // safe to upcast - final Set tmpSet = (Set) set; - return tmpSet; - } - return new UnmodifiableSet<>(set); - } - - //----------------------------------------------------------------------- - /** - * Constructor that wraps (not copies). - * - * @param set the set to decorate, must not be null - * @throws NullPointerException if set is null - */ - @SuppressWarnings("unchecked") // safe to upcast - private UnmodifiableSet(final Set set) { - super((Set) set); - } - - //----------------------------------------------------------------------- - @Override - public Iterator iterator() { - return UnmodifiableIterator.unmodifiableIterator(decorated().iterator()); - } - - @Override - public boolean add(final E object) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(final Object object) { - throw new UnsupportedOperationException(); - } - - /** - * @since 4.4 - */ - @Override - public boolean removeIf(Predicate filter) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean retainAll(final Collection coll) { - throw new UnsupportedOperationException(); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openjpa.lib.util.collections; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.function.Predicate; + +/** + * Decorates another Set to ensure it can't be altered. + *

+ * This class is Serializable from Commons Collections 3.1. + *

+ *

+ * Attempts to modify it will result in an UnsupportedOperationException. + *

+ * + * @param the type of the elements in this set + * @since 3.0 + */ +public final class UnmodifiableSet + extends AbstractSerializableSetDecorator + implements Unmodifiable { + + /** Serialization version */ + private static final long serialVersionUID = 6499119872185240161L; + + /** + * Factory method to create an unmodifiable set. + * + * @param the element type + * @param set the set to decorate, must not be null + * @return a new unmodifiable set + * @throws NullPointerException if set is null + * @since 4.0 + */ + public static Set unmodifiableSet(final Set set) { + if (set instanceof Unmodifiable) { + @SuppressWarnings("unchecked") // safe to upcast + final Set tmpSet = (Set) set; + return tmpSet; + } + return new UnmodifiableSet<>(set); + } + + //----------------------------------------------------------------------- + /** + * Constructor that wraps (not copies). + * + * @param set the set to decorate, must not be null + * @throws NullPointerException if set is null + */ + @SuppressWarnings("unchecked") // safe to upcast + private UnmodifiableSet(final Set set) { + super((Set) set); + } + + //----------------------------------------------------------------------- + @Override + public Iterator iterator() { + return UnmodifiableIterator.unmodifiableIterator(decorated().iterator()); + } + + @Override + public boolean add(final E object) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(final Object object) { + throw new UnsupportedOperationException(); + } + + /** + * @since 4.4 + */ + @Override + public boolean removeIf(Predicate filter) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jira1794/TestAggregateFunctions.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jira1794/TestAggregateFunctions.java index ed8feec429..360152209c 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jira1794/TestAggregateFunctions.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jira1794/TestAggregateFunctions.java @@ -1,289 +1,289 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.jira1794; - -import java.util.List; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; -import jakarta.persistence.TypedQuery; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Expression; -import jakarta.persistence.criteria.Path; -import jakarta.persistence.criteria.Root; -import jakarta.persistence.metamodel.Metamodel; -import jakarta.persistence.metamodel.SingularAttribute; - -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -/** - * OPENJPA-1794 Verifies the return value of aggregate functions when a query - * result set is empty. In this set of variations, the compatibility flag is not - * set so null is expected. - */ -public class TestAggregateFunctions extends SingleEMFTestCase { - - private static final int MAX = 0; - private static final int MIN = 1; - private static final int SUM = 2; - - private static final String[] numericAggregateFunctions = { "MAX", "AVG", - "MIN", "SUM" }; - - private static final String[] stringAggregateFunctions = { "MAX", "MIN" }; - - private static final String[] numericAttributes = { "ae.pintVal", - "ae.intVal", "ae.shortVal", "ae.pshortVal", "ae.pintVal", - "ae.intVal", "ae.plongVal", "ae.longVal", "ae.pfloatVal", - "ae.floatVal", "ae.pdblVal", "ae.dblVal" }; - - @Override - public void setUp() { - super.setUp(CLEAR_TABLES, AggEntity.class); - } - - protected boolean nullResultExpected() { - return true; - } - - public void testAggregateJPQL() { - EntityManager em = emf.createEntityManager(); - - // Verify all numeric types for all aggregate functions return null - // if there is no query result - verifyResult(em, numericAggregateFunctions, numericAttributes, true); - - // Verify a string for all applicable aggregate functions return null - // if there is no query result - verifyResult(em, stringAggregateFunctions, - new String[] { "ae.stringVal" }, true, true); - - verifyResult(em, stringAggregateFunctions, - new String[]{"ae.utilDate", "ae.sqlDate"}, true, true); - - // Add a row to the table and re-test - AggEntity ae = new AggEntity(); - ae.init(); - em.getTransaction().begin(); - em.persist(ae); - em.getTransaction().commit(); - - verifyResult(em, stringAggregateFunctions, "ae.sqlDate", java.sql.Date.class); - - // Verify all numeric types for all aggregate functions return a - // non-null value when there is a query result - verifyResult(em, numericAggregateFunctions, numericAttributes, false); - // Verify string types for all applicable aggregate functions return a - // non-null value when there is a query result - verifyResult(em, stringAggregateFunctions, - new String[] { "ae.stringVal" }, false); - - verifyResult(em, stringAggregateFunctions, "ae.utilDate", java.util.Date.class); - - em.close(); - } - - /** - * if a SUM(CASE statement is used, then the effective type might be different - */ - public void testAggregateWithCase() { - EntityManager em = emf.createEntityManager(); - - // Add a row to the table and re-test - em.getTransaction().begin(); - AggEntity ae = new AggEntity(); - ae.init(); - ae.setStringVal("bare"); - em.persist(ae); - AggEntity ae2 = new AggEntity(); - ae2.init(); - ae2.setStringVal("foot"); - em.persist(ae2); - em.getTransaction().commit(); - - em.getTransaction().begin(); - final TypedQuery q2 = em.createQuery("select SUM(ae.intVal) from AggEntity AS ae", Long.class); - final Long sum = q2.getSingleResult(); - assertEquals(2L, (long) sum); - - final TypedQuery q = em.createQuery("select SUM(CASE ae.stringVal WHEN 'bare' THEN 1 ELSE 0 END) from AggEntity AS ae", Long.class); - final Long sumC = q.getSingleResult(); - assertEquals(1L, (long) sumC); - - em.getTransaction().commit(); - - em.close(); - } - - public void testAggregateCriteria() { - EntityManager em = emf.createEntityManager(); - Metamodel mm = emf.getMetamodel(); - mm.getEntities(); - - Query q = null; - // Verify all types of criteria query that return a Numeric type - for (int agg = MAX; agg <= SUM; agg++) { - CriteriaQuery cqs = buildNumericCriteriaQuery(em, - Short.class, AggEntity_.shortVal, agg); - q = em.createQuery(cqs); - verifyQueryResult(q, true); - - cqs = buildNumericCriteriaQuery(em, Short.class, - AggEntity_.pshortVal, agg); - q = em.createQuery(cqs); - verifyQueryResult(q, true); - - CriteriaQuery cqi = buildNumericCriteriaQuery(em, - Integer.class, AggEntity_.intVal, agg); - q = em.createQuery(cqi); - verifyQueryResult(q, true); - - cqi = buildNumericCriteriaQuery(em, Integer.class, - AggEntity_.pintVal, agg); - q = em.createQuery(cqi); - verifyQueryResult(q, true); - - CriteriaQuery cqf = buildNumericCriteriaQuery(em, - Float.class, AggEntity_.floatVal, agg); - q = em.createQuery(cqf); - verifyQueryResult(q, true); - - cqf = buildNumericCriteriaQuery(em, Float.class, - AggEntity_.pfloatVal, agg); - q = em.createQuery(cqi); - verifyQueryResult(q, true); - - CriteriaQuery cqd = buildNumericCriteriaQuery(em, - Double.class, AggEntity_.dblVal, agg); - q = em.createQuery(cqd); - verifyQueryResult(q, true); - - cqd = buildNumericCriteriaQuery(em, Double.class, - AggEntity_.pdblVal, agg); - q = em.createQuery(cqi); - verifyQueryResult(q, true); - } - - // Verify AVG criteria query - it strictly returns type 'Double' so - // unlike other aggregates, - // it cannot be handled generically (as Numeric). - CriteriaQuery cqd = buildAvgCriteriaQuery(em, Double.class, - AggEntity_.dblVal); - q = em.createQuery(cqd); - verifyQueryResult(q, true); - - cqd = buildAvgCriteriaQuery(em, Double.class, AggEntity_.pdblVal); - q = em.createQuery(cqd); - verifyQueryResult(q, true); - - em.close(); - } - - private CriteriaQuery buildNumericCriteriaQuery( - EntityManager em, Class type, - SingularAttribute sa, int at) { - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(type); - Root aer = cq.from(AggEntity.class); - Path path = aer.get(sa); - Expression exp = null; - switch (at) { - case MAX: - exp = cb.max(path); - break; - case MIN: - exp = cb.min(path); - break; - case SUM: - exp = cb.sum(path); - break; - } - cq.select(exp); - return cq; - } - - private CriteriaQuery buildAvgCriteriaQuery(EntityManager em, - Class type, SingularAttribute sa) { - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(type); - Root aer = cq.from(AggEntity.class); - return cq.select(cb.avg(aer.get(sa))); - } - - private void verifyResult(EntityManager em, String[] aggregates, - String[] attributes, boolean expectNull) { - verifyResult(em, aggregates, attributes, expectNull, false); - } - - private void verifyResult(EntityManager em, String[] aggregates, - String[] attributes, boolean expectNull, boolean isString) { - for (String func : aggregates) { - for (String attr : attributes) { - // JPQL with aggregate and aggregate in subselect - String sql = "SELECT " + func + "(" + attr + ")" - + " FROM AggEntity ae WHERE " + attr + " <= " - + "(SELECT " + func + "(" - + attr.replaceFirst("^ae.", "ae2.") - + ") FROM AggEntity ae2)"; - Query q = em.createQuery(sql); - verifyQueryResult(q, expectNull, isString); - } - } - } - - private void verifyResult(EntityManager em, String[] aggregates, - String attr, Class expectedType) { - for (String func : aggregates) { - // JPQL with aggregate and aggregate in subselect - String sql = "SELECT " + func + "(" + attr + ")" - + " FROM AggEntity ae WHERE " + attr + " <= " - + "(SELECT " + func + "(" - + attr.replaceFirst("^ae.", "ae2.") - + ") FROM AggEntity ae2)"; - TypedQuery q = em.createQuery(sql, expectedType); - T intance = q.getSingleResult(); - assertEquals(intance.getClass(), expectedType); - } - } - - private void verifyQueryResult(Query q, boolean emptyRs) { - verifyQueryResult(q, emptyRs, false); - } - - private void verifyQueryResult(Query q, boolean emptyRs, boolean isString) { - Object result = q.getSingleResult(); - if (!emptyRs && !isString) { - assertNotNull(result); - } else if (isString || nullResultExpected()) { - assertNull(result); - } else { - assertNotNull(result); - } - List resultList = q.getResultList(); - assertEquals(1, resultList.size()); - if (!emptyRs && !isString) { - assertNotNull(result); - } else if (isString || nullResultExpected()) { - assertNull(result); - } else { - assertNotNull(result); - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.jira1794; + +import java.util.List; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.SingularAttribute; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +/** + * OPENJPA-1794 Verifies the return value of aggregate functions when a query + * result set is empty. In this set of variations, the compatibility flag is not + * set so null is expected. + */ +public class TestAggregateFunctions extends SingleEMFTestCase { + + private static final int MAX = 0; + private static final int MIN = 1; + private static final int SUM = 2; + + private static final String[] numericAggregateFunctions = { "MAX", "AVG", + "MIN", "SUM" }; + + private static final String[] stringAggregateFunctions = { "MAX", "MIN" }; + + private static final String[] numericAttributes = { "ae.pintVal", + "ae.intVal", "ae.shortVal", "ae.pshortVal", "ae.pintVal", + "ae.intVal", "ae.plongVal", "ae.longVal", "ae.pfloatVal", + "ae.floatVal", "ae.pdblVal", "ae.dblVal" }; + + @Override + public void setUp() { + super.setUp(CLEAR_TABLES, AggEntity.class); + } + + protected boolean nullResultExpected() { + return true; + } + + public void testAggregateJPQL() { + EntityManager em = emf.createEntityManager(); + + // Verify all numeric types for all aggregate functions return null + // if there is no query result + verifyResult(em, numericAggregateFunctions, numericAttributes, true); + + // Verify a string for all applicable aggregate functions return null + // if there is no query result + verifyResult(em, stringAggregateFunctions, + new String[] { "ae.stringVal" }, true, true); + + verifyResult(em, stringAggregateFunctions, + new String[]{"ae.utilDate", "ae.sqlDate"}, true, true); + + // Add a row to the table and re-test + AggEntity ae = new AggEntity(); + ae.init(); + em.getTransaction().begin(); + em.persist(ae); + em.getTransaction().commit(); + + verifyResult(em, stringAggregateFunctions, "ae.sqlDate", java.sql.Date.class); + + // Verify all numeric types for all aggregate functions return a + // non-null value when there is a query result + verifyResult(em, numericAggregateFunctions, numericAttributes, false); + // Verify string types for all applicable aggregate functions return a + // non-null value when there is a query result + verifyResult(em, stringAggregateFunctions, + new String[] { "ae.stringVal" }, false); + + verifyResult(em, stringAggregateFunctions, "ae.utilDate", java.util.Date.class); + + em.close(); + } + + /** + * if a SUM(CASE statement is used, then the effective type might be different + */ + public void testAggregateWithCase() { + EntityManager em = emf.createEntityManager(); + + // Add a row to the table and re-test + em.getTransaction().begin(); + AggEntity ae = new AggEntity(); + ae.init(); + ae.setStringVal("bare"); + em.persist(ae); + AggEntity ae2 = new AggEntity(); + ae2.init(); + ae2.setStringVal("foot"); + em.persist(ae2); + em.getTransaction().commit(); + + em.getTransaction().begin(); + final TypedQuery q2 = em.createQuery("select SUM(ae.intVal) from AggEntity AS ae", Long.class); + final Long sum = q2.getSingleResult(); + assertEquals(2L, (long) sum); + + final TypedQuery q = em.createQuery("select SUM(CASE ae.stringVal WHEN 'bare' THEN 1 ELSE 0 END) from AggEntity AS ae", Long.class); + final Long sumC = q.getSingleResult(); + assertEquals(1L, (long) sumC); + + em.getTransaction().commit(); + + em.close(); + } + + public void testAggregateCriteria() { + EntityManager em = emf.createEntityManager(); + Metamodel mm = emf.getMetamodel(); + mm.getEntities(); + + Query q = null; + // Verify all types of criteria query that return a Numeric type + for (int agg = MAX; agg <= SUM; agg++) { + CriteriaQuery cqs = buildNumericCriteriaQuery(em, + Short.class, AggEntity_.shortVal, agg); + q = em.createQuery(cqs); + verifyQueryResult(q, true); + + cqs = buildNumericCriteriaQuery(em, Short.class, + AggEntity_.pshortVal, agg); + q = em.createQuery(cqs); + verifyQueryResult(q, true); + + CriteriaQuery cqi = buildNumericCriteriaQuery(em, + Integer.class, AggEntity_.intVal, agg); + q = em.createQuery(cqi); + verifyQueryResult(q, true); + + cqi = buildNumericCriteriaQuery(em, Integer.class, + AggEntity_.pintVal, agg); + q = em.createQuery(cqi); + verifyQueryResult(q, true); + + CriteriaQuery cqf = buildNumericCriteriaQuery(em, + Float.class, AggEntity_.floatVal, agg); + q = em.createQuery(cqf); + verifyQueryResult(q, true); + + cqf = buildNumericCriteriaQuery(em, Float.class, + AggEntity_.pfloatVal, agg); + q = em.createQuery(cqi); + verifyQueryResult(q, true); + + CriteriaQuery cqd = buildNumericCriteriaQuery(em, + Double.class, AggEntity_.dblVal, agg); + q = em.createQuery(cqd); + verifyQueryResult(q, true); + + cqd = buildNumericCriteriaQuery(em, Double.class, + AggEntity_.pdblVal, agg); + q = em.createQuery(cqi); + verifyQueryResult(q, true); + } + + // Verify AVG criteria query - it strictly returns type 'Double' so + // unlike other aggregates, + // it cannot be handled generically (as Numeric). + CriteriaQuery cqd = buildAvgCriteriaQuery(em, Double.class, + AggEntity_.dblVal); + q = em.createQuery(cqd); + verifyQueryResult(q, true); + + cqd = buildAvgCriteriaQuery(em, Double.class, AggEntity_.pdblVal); + q = em.createQuery(cqd); + verifyQueryResult(q, true); + + em.close(); + } + + private CriteriaQuery buildNumericCriteriaQuery( + EntityManager em, Class type, + SingularAttribute sa, int at) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(type); + Root aer = cq.from(AggEntity.class); + Path path = aer.get(sa); + Expression exp = null; + switch (at) { + case MAX: + exp = cb.max(path); + break; + case MIN: + exp = cb.min(path); + break; + case SUM: + exp = cb.sum(path); + break; + } + cq.select(exp); + return cq; + } + + private CriteriaQuery buildAvgCriteriaQuery(EntityManager em, + Class type, SingularAttribute sa) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(type); + Root aer = cq.from(AggEntity.class); + return cq.select(cb.avg(aer.get(sa))); + } + + private void verifyResult(EntityManager em, String[] aggregates, + String[] attributes, boolean expectNull) { + verifyResult(em, aggregates, attributes, expectNull, false); + } + + private void verifyResult(EntityManager em, String[] aggregates, + String[] attributes, boolean expectNull, boolean isString) { + for (String func : aggregates) { + for (String attr : attributes) { + // JPQL with aggregate and aggregate in subselect + String sql = "SELECT " + func + "(" + attr + ")" + + " FROM AggEntity ae WHERE " + attr + " <= " + + "(SELECT " + func + "(" + + attr.replaceFirst("^ae.", "ae2.") + + ") FROM AggEntity ae2)"; + Query q = em.createQuery(sql); + verifyQueryResult(q, expectNull, isString); + } + } + } + + private void verifyResult(EntityManager em, String[] aggregates, + String attr, Class expectedType) { + for (String func : aggregates) { + // JPQL with aggregate and aggregate in subselect + String sql = "SELECT " + func + "(" + attr + ")" + + " FROM AggEntity ae WHERE " + attr + " <= " + + "(SELECT " + func + "(" + + attr.replaceFirst("^ae.", "ae2.") + + ") FROM AggEntity ae2)"; + TypedQuery q = em.createQuery(sql, expectedType); + T intance = q.getSingleResult(); + assertEquals(intance.getClass(), expectedType); + } + } + + private void verifyQueryResult(Query q, boolean emptyRs) { + verifyQueryResult(q, emptyRs, false); + } + + private void verifyQueryResult(Query q, boolean emptyRs, boolean isString) { + Object result = q.getSingleResult(); + if (!emptyRs && !isString) { + assertNotNull(result); + } else if (isString || nullResultExpected()) { + assertNull(result); + } else { + assertNotNull(result); + } + List resultList = q.getResultList(); + assertEquals(1, resultList.size()); + if (!emptyRs && !isString) { + assertNotNull(result); + } else if (isString || nullResultExpected()) { + assertNull(result); + } else { + assertNotNull(result); + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jira1794/TestCompatAggregateFunctions.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jira1794/TestCompatAggregateFunctions.java index f57126a94b..68ed9cef34 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jira1794/TestCompatAggregateFunctions.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jira1794/TestCompatAggregateFunctions.java @@ -1,41 +1,41 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.jira1794; - -/** - * OPENJPA-1794 - * Verifies the return value of aggregate functions when a query result - * set is empty. In this variation, the compatibility flag - * is set so 0 is expected. - */ -public class TestCompatAggregateFunctions extends TestAggregateFunctions { - - @Override - public void setUp() { - super.setUp(CLEAR_TABLES, - "openjpa.Compatibility", "ReturnNullOnAggregateResult=false", - AggEntity.class); - } - - @Override - // In compatibility mode a null result is not expected. - protected boolean nullResultExpected() { - return false; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.jira1794; + +/** + * OPENJPA-1794 + * Verifies the return value of aggregate functions when a query result + * set is empty. In this variation, the compatibility flag + * is set so 0 is expected. + */ +public class TestCompatAggregateFunctions extends TestAggregateFunctions { + + @Override + public void setUp() { + super.setUp(CLEAR_TABLES, + "openjpa.Compatibility", "ReturnNullOnAggregateResult=false", + AggEntity.class); + } + + @Override + // In compatibility mode a null result is not expected. + protected boolean nullResultExpected() { + return false; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/PChild.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/PChild.java index 75f826889a..8863adbcdf 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/PChild.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/PChild.java @@ -1,56 +1,56 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.meta; - -import jakarta.persistence.Basic; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -@Entity -public class PChild { - @Id - @GeneratedValue - int idChild; - - @Version - int version; - - @Basic - String basic; -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.meta; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +@Entity +public class PChild { + @Id + @GeneratedValue + int idChild; + + @Version + int version; + + @Basic + String basic; +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/Parent.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/Parent.java index 9ab8675fb3..5f52daf4ee 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/Parent.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/Parent.java @@ -1,52 +1,52 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.meta; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToOne; -import jakarta.persistence.PrimaryKeyJoinColumn; -import jakarta.persistence.SecondaryTable; - -@Entity(name="META_PARENT") -@SecondaryTable(name = "ParentSecondaryTable", pkJoinColumns = - { @PrimaryKeyJoinColumn(name = "idParent", referencedColumnName = "idParent") }) -public class Parent { - - @Id - @GeneratedValue - int idParent; - - String child_ref; - - @OneToOne - @JoinColumn(name = "CHILD_REF", table = "ParentSecondaryTable", referencedColumnName = "idChild") - PChild child; - - @OneToOne - @JoinColumn(name = "CHILDBI_REF", table = "ParentSecondaryTable", referencedColumnName = "idChild") - PChildBi childbi; - - @ManyToOne - @JoinColumn(name = "CHILDREN_REF", table = "ParentSecondaryTable", referencedColumnName = "idChild") - PChild children; -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.meta; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import jakarta.persistence.PrimaryKeyJoinColumn; +import jakarta.persistence.SecondaryTable; + +@Entity(name="META_PARENT") +@SecondaryTable(name = "ParentSecondaryTable", pkJoinColumns = + { @PrimaryKeyJoinColumn(name = "idParent", referencedColumnName = "idParent") }) +public class Parent { + + @Id + @GeneratedValue + int idParent; + + String child_ref; + + @OneToOne + @JoinColumn(name = "CHILD_REF", table = "ParentSecondaryTable", referencedColumnName = "idChild") + PChild child; + + @OneToOne + @JoinColumn(name = "CHILDBI_REF", table = "ParentSecondaryTable", referencedColumnName = "idChild") + PChildBi childbi; + + @ManyToOne + @JoinColumn(name = "CHILDREN_REF", table = "ParentSecondaryTable", referencedColumnName = "idChild") + PChild children; +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/TestSecondaryTable.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/TestSecondaryTable.java index 5f60f18a7b..5f259b373c 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/TestSecondaryTable.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/meta/TestSecondaryTable.java @@ -1,49 +1,49 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.meta; - -import org.apache.openjpa.jdbc.meta.FieldMapping; -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -public class TestSecondaryTable extends SingleEMFTestCase { - @Override - public void setUp() { - setUp(Parent.class, PChild.class, PChildBi.class - // Hard code to 2.0 p.xml value. If the p.xml is 1.0, this value will be changed to false, and the test - // won't fail. - , "openjpa.Compatibility", "NonDefaultMappingAllowed=true"); - } - - /** - * Added for OPENJPA-2247. - */ - public void testMappingInfo() { - FieldMapping fm = getMapping(Parent.class).getFieldMapping("child"); - assertNotNull(fm); - assertEquals("CHILD_REF", fm.getColumns()[0].getIdentifier().getName()); - - fm = getMapping(Parent.class).getFieldMapping("childbi"); - assertNotNull(fm); - assertEquals("CHILDBI_REF", fm.getColumns()[0].getIdentifier().getName()); - - fm = getMapping(Parent.class).getFieldMapping("children"); - assertNotNull(fm); - assertEquals("CHILDREN_REF", fm.getColumns()[0].getIdentifier().getName()); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.meta; + +import org.apache.openjpa.jdbc.meta.FieldMapping; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestSecondaryTable extends SingleEMFTestCase { + @Override + public void setUp() { + setUp(Parent.class, PChild.class, PChildBi.class + // Hard code to 2.0 p.xml value. If the p.xml is 1.0, this value will be changed to false, and the test + // won't fail. + , "openjpa.Compatibility", "NonDefaultMappingAllowed=true"); + } + + /** + * Added for OPENJPA-2247. + */ + public void testMappingInfo() { + FieldMapping fm = getMapping(Parent.class).getFieldMapping("child"); + assertNotNull(fm); + assertEquals("CHILD_REF", fm.getColumns()[0].getIdentifier().getName()); + + fm = getMapping(Parent.class).getFieldMapping("childbi"); + assertNotNull(fm); + assertEquals("CHILDBI_REF", fm.getColumns()[0].getIdentifier().getName()); + + fm = getMapping(Parent.class).getFieldMapping("children"); + assertNotNull(fm); + assertEquals("CHILDREN_REF", fm.getColumns()[0].getIdentifier().getName()); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/EntityListenerMappedSuperClass.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/EntityListenerMappedSuperClass.java index 594686a962..436b4b1053 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/EntityListenerMappedSuperClass.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/EntityListenerMappedSuperClass.java @@ -1,29 +1,29 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.openjpa.persistence.callbacks; - -import jakarta.persistence.EntityListeners; -import jakarta.persistence.MappedSuperclass; - -@MappedSuperclass -@EntityListeners({ListenerImpl.class}) -public abstract class EntityListenerMappedSuperClass { - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.openjpa.persistence.callbacks; + +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +@EntityListeners({ListenerImpl.class}) +public abstract class EntityListenerMappedSuperClass { + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MSCListenerEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MSCListenerEntity.java index 34d1fad6d1..38cc0aadb7 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MSCListenerEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/MSCListenerEntity.java @@ -1,50 +1,50 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.callbacks; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; - -@Entity -public class MSCListenerEntity extends EntityListenerMappedSuperClass implements ListenerTestEntity { - @Id @GeneratedValue - private long id; - - private int value; - - @Override - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - @Override - public int getValue() { - return value; - } - - @Override - public void setValue(int value) { - this.value = value; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.callbacks; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; + +@Entity +public class MSCListenerEntity extends EntityListenerMappedSuperClass implements ListenerTestEntity { + @Id @GeneratedValue + private long id; + + private int value; + + @Override + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @Override + public int getValue() { + return value; + } + + @Override + public void setValue(int value) { + this.value = value; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMSCEntityListeners.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMSCEntityListeners.java index 06cdddc5de..0779e6832a 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMSCEntityListeners.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/callbacks/TestMSCEntityListeners.java @@ -1,120 +1,120 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.callbacks; - -import org.apache.openjpa.persistence.OpenJPAEntityManager; -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -/* - * Separate testcase from TestEntityListeners to avoid the declaration of - * a System level event listener in the orm.xml file... - */ -public class TestMSCEntityListeners extends SingleEMFTestCase { - - private static final int MSC_LISTENER_ENTITY = 4; - - @Override - public void setUp() { - setUp(CLEAR_TABLES); - ListenerImpl.prePersistCount = 0; - ListenerImpl.postPersistCount = 0; - ListenerImpl.preUpdateCount = 0; - ListenerImpl.postUpdateCount = 0; - ListenerImpl.preRemoveCount = 0; - ListenerImpl.postRemoveCount = 0; - ListenerImpl.postLoadCount = 0; - } - - @Override - protected String getPersistenceUnitName() { - return "msclistener-pu"; - } - - public void testMSCEntityListeners() { - helper(MSC_LISTENER_ENTITY); - } - - public void helper(int entityListeners) { - OpenJPAEntityManager em = emf.createEntityManager(); - try { - em.getTransaction().begin(); - ListenerTestEntity o = null; - switch (entityListeners) { - case MSC_LISTENER_ENTITY: - o = new MSCListenerEntity(); - break; - - } - em.persist(o); - - assertStatus(1, 0, 0, 0, 0, 0, 0); - - em.getTransaction().commit(); - long id = o.getId(); - em.close(); - - assertStatus(1, 1, 0, 0, 0, 0, 0); - - em = emf.createEntityManager(); - em.getTransaction().begin(); - switch (entityListeners) { - case MSC_LISTENER_ENTITY: - o = em.find(MSCListenerEntity.class, id); - break; - - } - assertNotNull(o); - assertStatus(1, 1, 0, 0, 0, 0, 1); - - o.setValue(o.getValue() + 1); - - em.flush(); - assertStatus(1, 1, 1, 1, 0, 0, 1); - - em.remove(o); - assertStatus(1, 1, 1, 1, 1, 0, 1); - - em.getTransaction().commit(); - - assertStatus(1, 1, 1, 1, 1, 1, 1); - - em.close(); - } finally { - if (em != null && em.getTransaction().isActive()) - em.getTransaction().rollback(); - if (em != null && em.isOpen()) - em.close(); - } - } - - private void assertStatus( - int prePersist, int postPersist, - int preUpdate, int postUpdate, - int preRemove, int postRemove, - int postLoad) { - assertEquals(prePersist, ListenerImpl.prePersistCount); - assertEquals(postPersist, ListenerImpl.postPersistCount); - assertEquals(preUpdate, ListenerImpl.preUpdateCount); - assertEquals(postUpdate, ListenerImpl.postUpdateCount); - assertEquals(preRemove, ListenerImpl.preRemoveCount); - assertEquals(postRemove, ListenerImpl.postRemoveCount); - assertEquals(postLoad, ListenerImpl.postLoadCount); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.callbacks; + +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +/* + * Separate testcase from TestEntityListeners to avoid the declaration of + * a System level event listener in the orm.xml file... + */ +public class TestMSCEntityListeners extends SingleEMFTestCase { + + private static final int MSC_LISTENER_ENTITY = 4; + + @Override + public void setUp() { + setUp(CLEAR_TABLES); + ListenerImpl.prePersistCount = 0; + ListenerImpl.postPersistCount = 0; + ListenerImpl.preUpdateCount = 0; + ListenerImpl.postUpdateCount = 0; + ListenerImpl.preRemoveCount = 0; + ListenerImpl.postRemoveCount = 0; + ListenerImpl.postLoadCount = 0; + } + + @Override + protected String getPersistenceUnitName() { + return "msclistener-pu"; + } + + public void testMSCEntityListeners() { + helper(MSC_LISTENER_ENTITY); + } + + public void helper(int entityListeners) { + OpenJPAEntityManager em = emf.createEntityManager(); + try { + em.getTransaction().begin(); + ListenerTestEntity o = null; + switch (entityListeners) { + case MSC_LISTENER_ENTITY: + o = new MSCListenerEntity(); + break; + + } + em.persist(o); + + assertStatus(1, 0, 0, 0, 0, 0, 0); + + em.getTransaction().commit(); + long id = o.getId(); + em.close(); + + assertStatus(1, 1, 0, 0, 0, 0, 0); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + switch (entityListeners) { + case MSC_LISTENER_ENTITY: + o = em.find(MSCListenerEntity.class, id); + break; + + } + assertNotNull(o); + assertStatus(1, 1, 0, 0, 0, 0, 1); + + o.setValue(o.getValue() + 1); + + em.flush(); + assertStatus(1, 1, 1, 1, 0, 0, 1); + + em.remove(o); + assertStatus(1, 1, 1, 1, 1, 0, 1); + + em.getTransaction().commit(); + + assertStatus(1, 1, 1, 1, 1, 1, 1); + + em.close(); + } finally { + if (em != null && em.getTransaction().isActive()) + em.getTransaction().rollback(); + if (em != null && em.isOpen()) + em.close(); + } + } + + private void assertStatus( + int prePersist, int postPersist, + int preUpdate, int postUpdate, + int preRemove, int postRemove, + int postLoad) { + assertEquals(prePersist, ListenerImpl.prePersistCount); + assertEquals(postPersist, ListenerImpl.postPersistCount); + assertEquals(preUpdate, ListenerImpl.preUpdateCount); + assertEquals(postUpdate, ListenerImpl.postUpdateCount); + assertEquals(preRemove, ListenerImpl.preRemoveCount); + assertEquals(postRemove, ListenerImpl.postRemoveCount); + assertEquals(postLoad, ListenerImpl.postLoadCount); + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/Uni_1ToM_Map_FK.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/Uni_1ToM_Map_FK.java index d20cba8523..70793c7b66 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/Uni_1ToM_Map_FK.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/compat/Uni_1ToM_Map_FK.java @@ -1,91 +1,91 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.compat; - -import java.util.Collection; -import java.util.Map; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToMany; - -//non-default mapping -//Sec 11.1.36, Example 3: -// Unidirectional One-to-Many association using a foreign key mapping -// In Customer class: -// @OneToMany(orphanRemoval=true) -// @JoinColumn(name="CUST_ID") // join column is in table for Order -// public Set getOrders() {return orders;} - -@Entity -public class Uni_1ToM_Map_FK { - - @Id - @GeneratedValue - private long id; - - private String name; - - @OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER) - @JoinColumn(name="Uni1MFK_ID") - private Map entityCs = null; - - public long getId() { - return id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Map getEntityCs() { - return entityCs; - } - - public void setEntityCs(Map entityCs) { - this.entityCs = entityCs; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Uni_1ToM_Map_FK)) return false; - Uni_1ToM_Map_FK b = (Uni_1ToM_Map_FK)o; - if (!b.name.equals(name)) return false; - if (b.entityCs.size() != entityCs.size()) return false; - Collection coll = b.entityCs.values(); - for (EntityC_U1M_Map_FK c : coll) { - if (!b.entityCs.get(c.getName()).equals(entityCs.get(c.getName()))) - return false; - } - return true; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.compat; + +import java.util.Collection; +import java.util.Map; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; + +//non-default mapping +//Sec 11.1.36, Example 3: +// Unidirectional One-to-Many association using a foreign key mapping +// In Customer class: +// @OneToMany(orphanRemoval=true) +// @JoinColumn(name="CUST_ID") // join column is in table for Order +// public Set getOrders() {return orders;} + +@Entity +public class Uni_1ToM_Map_FK { + + @Id + @GeneratedValue + private long id; + + private String name; + + @OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER) + @JoinColumn(name="Uni1MFK_ID") + private Map entityCs = null; + + public long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getEntityCs() { + return entityCs; + } + + public void setEntityCs(Map entityCs) { + this.entityCs = entityCs; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Uni_1ToM_Map_FK)) return false; + Uni_1ToM_Map_FK b = (Uni_1ToM_Map_FK)o; + if (!b.name.equals(name)) return false; + if (b.entityCs.size() != entityCs.size()) return false; + Collection coll = b.entityCs.values(); + for (EntityC_U1M_Map_FK c : coll) { + if (!b.entityCs.get(c.getName()).equals(entityCs.get(c.getName()))) + return false; + } + return true; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestRemoteQueryCacheCriteriaQuery.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestRemoteQueryCacheCriteriaQuery.java index 33588e834c..adfb19b1f7 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestRemoteQueryCacheCriteriaQuery.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/TestRemoteQueryCacheCriteriaQuery.java @@ -1,61 +1,61 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.criteria; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Root; -import jakarta.persistence.metamodel.EntityType; -import jakarta.persistence.metamodel.Metamodel; - -import org.apache.openjpa.persistence.datacache.SerializingConcurrentQueryCache; -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -public class TestRemoteQueryCacheCriteriaQuery extends SingleEMFTestCase { - protected EntityType department_ = null; - protected OpenJPACriteriaBuilder cb; - - @Override - public void setUp() throws Exception { - super.setUp("openjpa.DataCache", "true", "openjpa.QueryCache", - SerializingConcurrentQueryCache.SERIALIZING_CONCURRENT_QUERY_CACHE, Department.class, Employee.class, - Contact.class, Manager.class, FrequentFlierPlan.class); - EntityManager em = emf.createEntityManager(); - try { - Metamodel mm = em.getMetamodel(); - department_ = mm.entity(Department.class); - cb = emf.getCriteriaBuilder(); - } finally { - em.close(); - } - } - - public void test() { - CriteriaQuery q = cb.createQuery(Department.class); - Root dept = q.from(Department.class); - q.select(dept).where(cb.equal(dept.get(department_.getSingularAttribute("name", String.class)), "test")); - - EntityManager em = emf.createEntityManager(); - try { - em.createQuery(q).getResultList(); - } finally { - em.close(); - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.criteria; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.Metamodel; + +import org.apache.openjpa.persistence.datacache.SerializingConcurrentQueryCache; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestRemoteQueryCacheCriteriaQuery extends SingleEMFTestCase { + protected EntityType department_ = null; + protected OpenJPACriteriaBuilder cb; + + @Override + public void setUp() throws Exception { + super.setUp("openjpa.DataCache", "true", "openjpa.QueryCache", + SerializingConcurrentQueryCache.SERIALIZING_CONCURRENT_QUERY_CACHE, Department.class, Employee.class, + Contact.class, Manager.class, FrequentFlierPlan.class); + EntityManager em = emf.createEntityManager(); + try { + Metamodel mm = em.getMetamodel(); + department_ = mm.entity(Department.class); + cb = emf.getCriteriaBuilder(); + } finally { + em.close(); + } + } + + public void test() { + CriteriaQuery q = cb.createQuery(Department.class); + Root dept = q.from(Department.class); + q.select(dept).where(cb.equal(dept.get(department_.getSingularAttribute("name", String.class)), "test")); + + EntityManager em = emf.createEntityManager(); + try { + em.createQuery(q).getResultList(); + } finally { + em.close(); + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/DimDay.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/DimDay.java index 38a21e5ff4..08ad829cd3 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/DimDay.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/DimDay.java @@ -1,452 +1,452 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.criteria.multiselect; - -import java.util.Date; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; - -@Entity -@Table(name="T_DIM_DAY") -public class DimDay { - - @Id - @Column(name="DAY_KY") - private Long key; - - @Column(name="DAY_DT") - private Date date; - - @Column(name = "DAY_FULL_NM") - private String dayFullName; - - @Column(name = "QTR_FULL_NM") - private String qtrFullName; - - @Column(name = "MNTH_FULL_NM") - private String monthFullName; - - @Column(name = "MNTH_SHRT_NM") - private String monthName; - - @Column(name = "YR_NBR") - private String year; - - @Column(name = "QTR_IN_YR_NBR") - private int quarterInYearNumber; - - @Column(name = "YR_CD") - private String yearCode; - - @Column(name = "QTR_CD") - private String quarterCode; - - @Column(name = "MNTH_CD") - private String monthCode; - - @Column(name = "MNTH_IN_YR_NBR") - private Long monthInYearNumber; - - @Column(name="CUR_MNTH_IND") - private Long currentMonthInd; - - @Column(name="CUR_QTR_IND") - private Long currentQtrInd; - - @Column(name="CUR_YR_IND") - private Long currentYearInd; - - @Column(name="PREV_MNTH_IND") - private Long prevMonthInd; - - @Column(name="PREV_QTR_IND") - private Long prevQtrInd; - - @Column(name="PREV_YR_IND") - private Long prevYearInd; - - @Column(name="CUR_MNTH_IN_PREV_YR_IND") - private Long currentMonthVsPrevYearInd; - - @Column(name="CUR_QTR_IN_PREV_YR_IND") - private Long currentQtrVsPrevYearInd; - - @Column(name="WK_IN_YR_NBR") - private Long weekInYear; - - @Column(name="WK_IN_YR_FULL_NM") - private String weekInYearFullNm; - - @Column(name = "DAY_IN_WK_NBR") - private Long dayInWeek; - - @Column(name = "DAY_IN_MNTH_NBR") - private Long dayInMonth; - - @Column(name = "DAY_IN_QTR_NBR") - private Long dayInQuarter; - - @Column(name = "DAY_IN_YR_NBR") - private Long dayInYear; - - @Column(name="EOM_IND") - private Long eomInd; - - @Column(name="EOQ_IND") - private Long eoqInd; - - @Column(name="EOY_IND") - private Long eoyInd; - - @Column(name = "ROLL_13_MNTH_IND") - private Long roll13MonthInd; - - @Column(name = "ROLL_4_YRS_IND") - private Long roll4YearsInd; - - @Column(name = "ROLL_5_QTRS_IND") - private Long roll5QuartersInd; - - @Column(name="MNTH_STRT_DAY_KY") - private Long monthStrtDate; - - @Column(name="MNTH_END_DAY_KY") - private Long monthEndDate; - - @Column(name="QTR_STRT_DAY_KY") - private Long quarterStrtDate; - - @Column(name="QTR_END_DAY_KY") - private Long quarterEndDate; - - @Column(name = "YR_STRT_DAY_KY") - private Long yearStrtDate; - - @Column(name = "YR_END_DAY_KY") - private Long yearEndDate; - - public Long getKey() { - return key; - } - - public void setKey(Long key) { - this.key = key; - } - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - this.date = date; - } - - public String getDayFullName() { - return dayFullName; - } - - public void setDayFullName(String dayFullName) { - this.dayFullName = dayFullName; - } - - public String getQtrFullName() { - return qtrFullName; - } - - public void setQtrFullName(String qtrFullName) { - this.qtrFullName = qtrFullName; - } - - public String getMonthName() { - return monthName; - } - - public void setMonthFullName(String monthName) { - this.monthName = monthName; - } - - public String getYear() { - return year; - } - - public void setYear(String year) { - this.year = year; - } - - public Long getCurrentMonthInd() { - return currentMonthInd; - } - - public void setCurrentMonthInd(Long currentMonthInd) { - this.currentMonthInd = currentMonthInd; - } - - public Long getCurrentQtrInd() { - return currentQtrInd; - } - - public void setCurrentQtrInd(Long currentQtrInd) { - this.currentQtrInd = currentQtrInd; - } - - public Long getCurrentYearInd() { - return currentYearInd; - } - - public void setCurrentYearInd(Long currentYearInd) { - this.currentYearInd = currentYearInd; - } - - public Long getPrevQtrInd() { - return prevQtrInd; - } - - public void setPrevQtrInd(Long prevQtrInd) { - this.prevQtrInd = prevQtrInd; - } - - public Long getPrevYearInd() { - return prevYearInd; - } - - public void setPrevYearInd(Long prevYearInd) { - this.prevYearInd = prevYearInd; - } - - public Long getCurrentMonthVsPrevYearInd() { - return currentMonthVsPrevYearInd; - } - - public void setCurrentMonthVsPrevYearInd(Long currentMonthVsPrevYearInd) { - this.currentMonthVsPrevYearInd = currentMonthVsPrevYearInd; - } - - public Long getCurrentQtrVsPrevYearInd() { - return currentQtrVsPrevYearInd; - } - - public void setCurrentQtrVsPrevYearInd(Long currentQtrVsPrevYearInd) { - this.currentQtrVsPrevYearInd = currentQtrVsPrevYearInd; - } - - public Long getPrevMonthInd() { - return prevMonthInd; - } - - public void setPrevMonthInd(Long prevMonthInd) { - this.prevMonthInd = prevMonthInd; - } - - public Long getWeekInYear() { - return weekInYear; - } - - public void setWeekInYear(Long weekInYear) { - this.weekInYear = weekInYear; - } - - public void setMonthName(String monthName) { - this.monthName = monthName; - } - - public Long getEomInd() { - return eomInd; - } - - public void setEomInd(Long eomInd) { - this.eomInd = eomInd; - } - - public String getYearCode() { - return yearCode; - } - - public void setYearCode(String yearCode) { - this.yearCode = yearCode; - } - - public String getQuarterCode() { - return quarterCode; - } - - public void setQuarterCode(String quarterCode) { - this.quarterCode = quarterCode; - } - - public String getMonthCode() { - return monthCode; - } - - public void setMonthCode(String monthCode) { - this.monthCode = monthCode; - } - - public String getMonthFullName() { - return monthFullName; - } - - public String getWeekInYearFullNm() { - return weekInYearFullNm; - } - - public void setWeekInYearFullNm(String weekInYearFullNm) { - this.weekInYearFullNm = weekInYearFullNm; - } - - public Long getMonthInYearNumber() { - return monthInYearNumber; - } - - public void setMonthInYearNumber(Long monthInYearNumber) { - this.monthInYearNumber = monthInYearNumber; - } - - public int getQuarterInYearNumber() { - return quarterInYearNumber; - } - - public void setQuarterInYearNumber(int quarterInYearNumber) { - this.quarterInYearNumber = quarterInYearNumber; - } - - public Long getRoll13MonthInd() { - return roll13MonthInd; - } - - public void setRoll13MonthInd(Long roll13MonthInd) { - this.roll13MonthInd = roll13MonthInd; - } - - public Long getRoll4YearsInd() { - return roll4YearsInd; - } - - public void setRoll4YearsInd(Long roll4YearsInd) { - this.roll4YearsInd = roll4YearsInd; - } - - public Long getRoll5QuartersInd() { - return roll5QuartersInd; - } - - public void setRoll5QuartersInd(Long roll5QuartersInd) { - this.roll5QuartersInd = roll5QuartersInd; - } - - public Long getDayInMonth() { - return dayInMonth; - } - - public void setDayInMonth(Long dayInMonth) { - this.dayInMonth = dayInMonth; - } - - public Long getDayInQuarter() { - return dayInQuarter; - } - - public void setDayInQuarter(Long dayInQuarter) { - this.dayInQuarter = dayInQuarter; - } - - public Long getDayInYear() { - return dayInYear; - } - - public void setDayInYear(Long dayInYear) { - this.dayInYear = dayInYear; - } - - public Long getEoqInd() { - return eoqInd; - } - - public void setEoqInd(Long eoqInd) { - this.eoqInd = eoqInd; - } - - public Long getEoyInd() { - return eoyInd; - } - - public void setEoyInd(Long eoyInd) { - this.eoyInd = eoyInd; - } - - public Long getMonthStrtDate() { - return monthStrtDate; - } - - public void setMonthStrtDate(Long monthStrtDate) { - this.monthStrtDate = monthStrtDate; - } - - public Long getMonthEndDate() { - return monthEndDate; - } - - public void setMonthEndDate(Long monthEndDate) { - this.monthEndDate = monthEndDate; - } - - public Long getQuarterStrtDate() { - return quarterStrtDate; - } - - public void setQuarterStrtDate(Long quarterStrtDate) { - this.quarterStrtDate = quarterStrtDate; - } - - public Long getQuarterEndDate() { - return quarterEndDate; - } - - public void setQuarterEndDate(Long quarterEndDate) { - this.quarterEndDate = quarterEndDate; - } - - public Long getYearStrtDate() { - return yearStrtDate; - } - - public void setYearStrtDate(Long yearStrtDate) { - this.yearStrtDate = yearStrtDate; - } - - public Long getYearEndDate() { - return yearEndDate; - } - - public void setYearEndDate(Long yearEndDate) { - this.yearEndDate = yearEndDate; - } - - public Long getDayInWeek() { - return dayInWeek; - } - - public void setDayInWeek(Long dayInWeek) { - this.dayInWeek = dayInWeek; - } - - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.criteria.multiselect; + +import java.util.Date; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@Entity +@Table(name="T_DIM_DAY") +public class DimDay { + + @Id + @Column(name="DAY_KY") + private Long key; + + @Column(name="DAY_DT") + private Date date; + + @Column(name = "DAY_FULL_NM") + private String dayFullName; + + @Column(name = "QTR_FULL_NM") + private String qtrFullName; + + @Column(name = "MNTH_FULL_NM") + private String monthFullName; + + @Column(name = "MNTH_SHRT_NM") + private String monthName; + + @Column(name = "YR_NBR") + private String year; + + @Column(name = "QTR_IN_YR_NBR") + private int quarterInYearNumber; + + @Column(name = "YR_CD") + private String yearCode; + + @Column(name = "QTR_CD") + private String quarterCode; + + @Column(name = "MNTH_CD") + private String monthCode; + + @Column(name = "MNTH_IN_YR_NBR") + private Long monthInYearNumber; + + @Column(name="CUR_MNTH_IND") + private Long currentMonthInd; + + @Column(name="CUR_QTR_IND") + private Long currentQtrInd; + + @Column(name="CUR_YR_IND") + private Long currentYearInd; + + @Column(name="PREV_MNTH_IND") + private Long prevMonthInd; + + @Column(name="PREV_QTR_IND") + private Long prevQtrInd; + + @Column(name="PREV_YR_IND") + private Long prevYearInd; + + @Column(name="CUR_MNTH_IN_PREV_YR_IND") + private Long currentMonthVsPrevYearInd; + + @Column(name="CUR_QTR_IN_PREV_YR_IND") + private Long currentQtrVsPrevYearInd; + + @Column(name="WK_IN_YR_NBR") + private Long weekInYear; + + @Column(name="WK_IN_YR_FULL_NM") + private String weekInYearFullNm; + + @Column(name = "DAY_IN_WK_NBR") + private Long dayInWeek; + + @Column(name = "DAY_IN_MNTH_NBR") + private Long dayInMonth; + + @Column(name = "DAY_IN_QTR_NBR") + private Long dayInQuarter; + + @Column(name = "DAY_IN_YR_NBR") + private Long dayInYear; + + @Column(name="EOM_IND") + private Long eomInd; + + @Column(name="EOQ_IND") + private Long eoqInd; + + @Column(name="EOY_IND") + private Long eoyInd; + + @Column(name = "ROLL_13_MNTH_IND") + private Long roll13MonthInd; + + @Column(name = "ROLL_4_YRS_IND") + private Long roll4YearsInd; + + @Column(name = "ROLL_5_QTRS_IND") + private Long roll5QuartersInd; + + @Column(name="MNTH_STRT_DAY_KY") + private Long monthStrtDate; + + @Column(name="MNTH_END_DAY_KY") + private Long monthEndDate; + + @Column(name="QTR_STRT_DAY_KY") + private Long quarterStrtDate; + + @Column(name="QTR_END_DAY_KY") + private Long quarterEndDate; + + @Column(name = "YR_STRT_DAY_KY") + private Long yearStrtDate; + + @Column(name = "YR_END_DAY_KY") + private Long yearEndDate; + + public Long getKey() { + return key; + } + + public void setKey(Long key) { + this.key = key; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getDayFullName() { + return dayFullName; + } + + public void setDayFullName(String dayFullName) { + this.dayFullName = dayFullName; + } + + public String getQtrFullName() { + return qtrFullName; + } + + public void setQtrFullName(String qtrFullName) { + this.qtrFullName = qtrFullName; + } + + public String getMonthName() { + return monthName; + } + + public void setMonthFullName(String monthName) { + this.monthName = monthName; + } + + public String getYear() { + return year; + } + + public void setYear(String year) { + this.year = year; + } + + public Long getCurrentMonthInd() { + return currentMonthInd; + } + + public void setCurrentMonthInd(Long currentMonthInd) { + this.currentMonthInd = currentMonthInd; + } + + public Long getCurrentQtrInd() { + return currentQtrInd; + } + + public void setCurrentQtrInd(Long currentQtrInd) { + this.currentQtrInd = currentQtrInd; + } + + public Long getCurrentYearInd() { + return currentYearInd; + } + + public void setCurrentYearInd(Long currentYearInd) { + this.currentYearInd = currentYearInd; + } + + public Long getPrevQtrInd() { + return prevQtrInd; + } + + public void setPrevQtrInd(Long prevQtrInd) { + this.prevQtrInd = prevQtrInd; + } + + public Long getPrevYearInd() { + return prevYearInd; + } + + public void setPrevYearInd(Long prevYearInd) { + this.prevYearInd = prevYearInd; + } + + public Long getCurrentMonthVsPrevYearInd() { + return currentMonthVsPrevYearInd; + } + + public void setCurrentMonthVsPrevYearInd(Long currentMonthVsPrevYearInd) { + this.currentMonthVsPrevYearInd = currentMonthVsPrevYearInd; + } + + public Long getCurrentQtrVsPrevYearInd() { + return currentQtrVsPrevYearInd; + } + + public void setCurrentQtrVsPrevYearInd(Long currentQtrVsPrevYearInd) { + this.currentQtrVsPrevYearInd = currentQtrVsPrevYearInd; + } + + public Long getPrevMonthInd() { + return prevMonthInd; + } + + public void setPrevMonthInd(Long prevMonthInd) { + this.prevMonthInd = prevMonthInd; + } + + public Long getWeekInYear() { + return weekInYear; + } + + public void setWeekInYear(Long weekInYear) { + this.weekInYear = weekInYear; + } + + public void setMonthName(String monthName) { + this.monthName = monthName; + } + + public Long getEomInd() { + return eomInd; + } + + public void setEomInd(Long eomInd) { + this.eomInd = eomInd; + } + + public String getYearCode() { + return yearCode; + } + + public void setYearCode(String yearCode) { + this.yearCode = yearCode; + } + + public String getQuarterCode() { + return quarterCode; + } + + public void setQuarterCode(String quarterCode) { + this.quarterCode = quarterCode; + } + + public String getMonthCode() { + return monthCode; + } + + public void setMonthCode(String monthCode) { + this.monthCode = monthCode; + } + + public String getMonthFullName() { + return monthFullName; + } + + public String getWeekInYearFullNm() { + return weekInYearFullNm; + } + + public void setWeekInYearFullNm(String weekInYearFullNm) { + this.weekInYearFullNm = weekInYearFullNm; + } + + public Long getMonthInYearNumber() { + return monthInYearNumber; + } + + public void setMonthInYearNumber(Long monthInYearNumber) { + this.monthInYearNumber = monthInYearNumber; + } + + public int getQuarterInYearNumber() { + return quarterInYearNumber; + } + + public void setQuarterInYearNumber(int quarterInYearNumber) { + this.quarterInYearNumber = quarterInYearNumber; + } + + public Long getRoll13MonthInd() { + return roll13MonthInd; + } + + public void setRoll13MonthInd(Long roll13MonthInd) { + this.roll13MonthInd = roll13MonthInd; + } + + public Long getRoll4YearsInd() { + return roll4YearsInd; + } + + public void setRoll4YearsInd(Long roll4YearsInd) { + this.roll4YearsInd = roll4YearsInd; + } + + public Long getRoll5QuartersInd() { + return roll5QuartersInd; + } + + public void setRoll5QuartersInd(Long roll5QuartersInd) { + this.roll5QuartersInd = roll5QuartersInd; + } + + public Long getDayInMonth() { + return dayInMonth; + } + + public void setDayInMonth(Long dayInMonth) { + this.dayInMonth = dayInMonth; + } + + public Long getDayInQuarter() { + return dayInQuarter; + } + + public void setDayInQuarter(Long dayInQuarter) { + this.dayInQuarter = dayInQuarter; + } + + public Long getDayInYear() { + return dayInYear; + } + + public void setDayInYear(Long dayInYear) { + this.dayInYear = dayInYear; + } + + public Long getEoqInd() { + return eoqInd; + } + + public void setEoqInd(Long eoqInd) { + this.eoqInd = eoqInd; + } + + public Long getEoyInd() { + return eoyInd; + } + + public void setEoyInd(Long eoyInd) { + this.eoyInd = eoyInd; + } + + public Long getMonthStrtDate() { + return monthStrtDate; + } + + public void setMonthStrtDate(Long monthStrtDate) { + this.monthStrtDate = monthStrtDate; + } + + public Long getMonthEndDate() { + return monthEndDate; + } + + public void setMonthEndDate(Long monthEndDate) { + this.monthEndDate = monthEndDate; + } + + public Long getQuarterStrtDate() { + return quarterStrtDate; + } + + public void setQuarterStrtDate(Long quarterStrtDate) { + this.quarterStrtDate = quarterStrtDate; + } + + public Long getQuarterEndDate() { + return quarterEndDate; + } + + public void setQuarterEndDate(Long quarterEndDate) { + this.quarterEndDate = quarterEndDate; + } + + public Long getYearStrtDate() { + return yearStrtDate; + } + + public void setYearStrtDate(Long yearStrtDate) { + this.yearStrtDate = yearStrtDate; + } + + public Long getYearEndDate() { + return yearEndDate; + } + + public void setYearEndDate(Long yearEndDate) { + this.yearEndDate = yearEndDate; + } + + public Long getDayInWeek() { + return dayInWeek; + } + + public void setDayInWeek(Long dayInWeek) { + this.dayInWeek = dayInWeek; + } + + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/FactWorkAssignment.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/FactWorkAssignment.java index 953a6baf3a..dd0430bc4b 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/FactWorkAssignment.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/FactWorkAssignment.java @@ -1,308 +1,308 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.criteria.multiselect; - -import java.util.Date; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; - -@Entity -@Table(name = "T_FACT_WORK_ASGNMT") -public class FactWorkAssignment { - @Id - @Column(name = "CLNT_OBJ_ID") - private String orgOID; - - @Column(name = "rec_eff_strt_dt") - private Date effStartDt; - - @Column(name = "rec_eff_end_dt") - private Date effEndDt; - - - @Column(name = "rec_eff_strt_day_ky") - private Long effectiveStartDate; - - @Column(name = "rec_eff_end_day_ky") - private Long effectiveEndDate; - - @Column(name = "pers_ky") - private Long personKey; - - @Column(name = "pers_obj_id") - private String personObjId; - - @Column(name = "prmry_work_asgnmt_ind") - private int primary; - - @Column(name = "empl_cnt") - private int employeeCount; - - @Column(name = "work_asgnmt_stus_cd") - private String statusCode; - - @Column(name="WORK_ASGNMT_STUS_DSC") - private String statusDesc; - - @Column(name="WORK_ASGNMT_NBR") - private String workAssgnmntNbr; - - @Column(name = "work_loc_ky") - private Long workLocationKey; - - @Column(name = "hr_orgn_ky") - private Long hrOrgKey; - - @Column(name = "PAYRL_ORGN_KY") - private Long payrollOrgKey; - - @Column(name = "job_ky") - private Long jobKey; - - @Column(name = "PERS_PRFL_ATTR_KY") - private Long personProfileKey; - - @Column(name = "mngr_ky") - private Long managerKey; - - @Column(name = "PAY_GRP_KY") - private Long paygroupKey; - - @Column(name = "SAL_PLAN_KY") - private Long salPlanKey; - - @Column(name = "COMPA_RT") - private Double compaRt; - - @Column(name="CLK_NBR") - private String clockNumber; - - @Column(name="DATA_CNTL_NBR") - private String dataCntrlNumber; - - @Column(name="SEC_CLR_CD") - private String secClrCd; - - @Column(name = "CUR_REC_IND") - private boolean currentRecord; - - public Long getSalPlanKey() { - return salPlanKey; - } - - public void setSalPlanKey(Long salPlanKey) { - this.salPlanKey = salPlanKey; - } - - public Long getManagerKey() { - return managerKey; - } - - public void setManagerKey(Long managerKey) { - this.managerKey = managerKey; - } - - public Long getPersonProfileKey() { - return personProfileKey; - } - - public void setPersonProfileKey(Long personProfileKey) { - this.personProfileKey = personProfileKey; - } - - public int getPrimary() { - return primary; - } - - public void setPrimary(int primary) { - this.primary = primary; - } - - public int getEmployeeCount() { - return employeeCount; - } - - public void setEmployeeCount(int employeeCount) { - this.employeeCount = employeeCount; - } - - public String getStatusCode() { - return statusCode; - } - - public void setStatusCode(String statusCode) { - this.statusCode = statusCode; - } - - public String getStatusDesc() { - return statusDesc; - } - - public void setStatusDesc(String statusDesc) { - this.statusDesc = statusDesc; - } - - public Long getWorkLocationKey() { - return workLocationKey; - } - - public void setWorkLocationKey(Long workLocationKey) { - this.workLocationKey = workLocationKey; - } - - public Long getHrOrgKey() { - return hrOrgKey; - } - - public void setHrOrgKey(Long hrOrgKey) { - this.hrOrgKey = hrOrgKey; - } - - public Long getJobKey() { - return jobKey; - } - - public void setJobKey(Long jobKey) { - this.jobKey = jobKey; - } - - public Long getPayrollOrgKey() { - return payrollOrgKey; - } - - public void setPayrollOrgKey(Long payrollOrgKey) { - this.payrollOrgKey = payrollOrgKey; - } - - public Double getCompaRt() { - return compaRt; - } - - public void setCompaRt(Double compaRt) { - this.compaRt = compaRt; - } - - public Long getPaygroupKey() { - return paygroupKey; - } - - public void setPaygroupKey(Long paygroupKey) { - this.paygroupKey = paygroupKey; - } - - public String getClockNumber() { - return clockNumber; - } - - public void setClockNumber(String clockNumber) { - this.clockNumber = clockNumber; - } - - public String getDataCntrlNumber() { - return dataCntrlNumber; - } - - public void setDataCntrlNumber(String dataCntrlNumber) { - this.dataCntrlNumber = dataCntrlNumber; - } - - public String getSecClrCd() { - return secClrCd; - } - - public void setSecClrCd(String secClrCd) { - this.secClrCd = secClrCd; - } - - public boolean isCurrentRecord() { - return currentRecord; - } - - public void setCurrentRecord(boolean currentRecord) { - this.currentRecord = currentRecord; - } - - public String getWorkAssgnmntNbr() { - return workAssgnmntNbr; - } - - public void setWorkAssgnmntNbr(String workAssgnmntNbr) { - this.workAssgnmntNbr = workAssgnmntNbr; - } - - public String getOrgOID() { - return orgOID; - } - - public void setOrgOID(String orgOID) { - this.orgOID = orgOID; - } - - public Long getEffectiveStartDate() { - return effectiveStartDate; - } - - public void setEffectiveStartDate(Long effectiveStartDate) { - this.effectiveStartDate = effectiveStartDate; - } - - public Long getEffectiveEndDate() { - return effectiveEndDate; - } - - public void setEffectiveEndDate(Long effectiveEndDate) { - this.effectiveEndDate = effectiveEndDate; - } - - public Long getPersonKey() { - return personKey; - } - - public void setPersonKey(Long personKey) { - this.personKey = personKey; - } - - public String getPersonObjId() { - return personObjId; - } - - public void setPersonObjId(String personObjId) { - this.personObjId = personObjId; - } - - public Date getEffStartDt() { - return effStartDt; - } - - public void setEffStartDt(Date effStartDt) { - this.effStartDt = effStartDt; - } - - public Date getEffEndDt() { - return effEndDt; - } - - public void setEffEndDt(Date effEndDt) { - this.effEndDt = effEndDt; - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.criteria.multiselect; + +import java.util.Date; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@Entity +@Table(name = "T_FACT_WORK_ASGNMT") +public class FactWorkAssignment { + @Id + @Column(name = "CLNT_OBJ_ID") + private String orgOID; + + @Column(name = "rec_eff_strt_dt") + private Date effStartDt; + + @Column(name = "rec_eff_end_dt") + private Date effEndDt; + + + @Column(name = "rec_eff_strt_day_ky") + private Long effectiveStartDate; + + @Column(name = "rec_eff_end_day_ky") + private Long effectiveEndDate; + + @Column(name = "pers_ky") + private Long personKey; + + @Column(name = "pers_obj_id") + private String personObjId; + + @Column(name = "prmry_work_asgnmt_ind") + private int primary; + + @Column(name = "empl_cnt") + private int employeeCount; + + @Column(name = "work_asgnmt_stus_cd") + private String statusCode; + + @Column(name="WORK_ASGNMT_STUS_DSC") + private String statusDesc; + + @Column(name="WORK_ASGNMT_NBR") + private String workAssgnmntNbr; + + @Column(name = "work_loc_ky") + private Long workLocationKey; + + @Column(name = "hr_orgn_ky") + private Long hrOrgKey; + + @Column(name = "PAYRL_ORGN_KY") + private Long payrollOrgKey; + + @Column(name = "job_ky") + private Long jobKey; + + @Column(name = "PERS_PRFL_ATTR_KY") + private Long personProfileKey; + + @Column(name = "mngr_ky") + private Long managerKey; + + @Column(name = "PAY_GRP_KY") + private Long paygroupKey; + + @Column(name = "SAL_PLAN_KY") + private Long salPlanKey; + + @Column(name = "COMPA_RT") + private Double compaRt; + + @Column(name="CLK_NBR") + private String clockNumber; + + @Column(name="DATA_CNTL_NBR") + private String dataCntrlNumber; + + @Column(name="SEC_CLR_CD") + private String secClrCd; + + @Column(name = "CUR_REC_IND") + private boolean currentRecord; + + public Long getSalPlanKey() { + return salPlanKey; + } + + public void setSalPlanKey(Long salPlanKey) { + this.salPlanKey = salPlanKey; + } + + public Long getManagerKey() { + return managerKey; + } + + public void setManagerKey(Long managerKey) { + this.managerKey = managerKey; + } + + public Long getPersonProfileKey() { + return personProfileKey; + } + + public void setPersonProfileKey(Long personProfileKey) { + this.personProfileKey = personProfileKey; + } + + public int getPrimary() { + return primary; + } + + public void setPrimary(int primary) { + this.primary = primary; + } + + public int getEmployeeCount() { + return employeeCount; + } + + public void setEmployeeCount(int employeeCount) { + this.employeeCount = employeeCount; + } + + public String getStatusCode() { + return statusCode; + } + + public void setStatusCode(String statusCode) { + this.statusCode = statusCode; + } + + public String getStatusDesc() { + return statusDesc; + } + + public void setStatusDesc(String statusDesc) { + this.statusDesc = statusDesc; + } + + public Long getWorkLocationKey() { + return workLocationKey; + } + + public void setWorkLocationKey(Long workLocationKey) { + this.workLocationKey = workLocationKey; + } + + public Long getHrOrgKey() { + return hrOrgKey; + } + + public void setHrOrgKey(Long hrOrgKey) { + this.hrOrgKey = hrOrgKey; + } + + public Long getJobKey() { + return jobKey; + } + + public void setJobKey(Long jobKey) { + this.jobKey = jobKey; + } + + public Long getPayrollOrgKey() { + return payrollOrgKey; + } + + public void setPayrollOrgKey(Long payrollOrgKey) { + this.payrollOrgKey = payrollOrgKey; + } + + public Double getCompaRt() { + return compaRt; + } + + public void setCompaRt(Double compaRt) { + this.compaRt = compaRt; + } + + public Long getPaygroupKey() { + return paygroupKey; + } + + public void setPaygroupKey(Long paygroupKey) { + this.paygroupKey = paygroupKey; + } + + public String getClockNumber() { + return clockNumber; + } + + public void setClockNumber(String clockNumber) { + this.clockNumber = clockNumber; + } + + public String getDataCntrlNumber() { + return dataCntrlNumber; + } + + public void setDataCntrlNumber(String dataCntrlNumber) { + this.dataCntrlNumber = dataCntrlNumber; + } + + public String getSecClrCd() { + return secClrCd; + } + + public void setSecClrCd(String secClrCd) { + this.secClrCd = secClrCd; + } + + public boolean isCurrentRecord() { + return currentRecord; + } + + public void setCurrentRecord(boolean currentRecord) { + this.currentRecord = currentRecord; + } + + public String getWorkAssgnmntNbr() { + return workAssgnmntNbr; + } + + public void setWorkAssgnmntNbr(String workAssgnmntNbr) { + this.workAssgnmntNbr = workAssgnmntNbr; + } + + public String getOrgOID() { + return orgOID; + } + + public void setOrgOID(String orgOID) { + this.orgOID = orgOID; + } + + public Long getEffectiveStartDate() { + return effectiveStartDate; + } + + public void setEffectiveStartDate(Long effectiveStartDate) { + this.effectiveStartDate = effectiveStartDate; + } + + public Long getEffectiveEndDate() { + return effectiveEndDate; + } + + public void setEffectiveEndDate(Long effectiveEndDate) { + this.effectiveEndDate = effectiveEndDate; + } + + public Long getPersonKey() { + return personKey; + } + + public void setPersonKey(Long personKey) { + this.personKey = personKey; + } + + public String getPersonObjId() { + return personObjId; + } + + public void setPersonObjId(String personObjId) { + this.personObjId = personObjId; + } + + public Date getEffStartDt() { + return effStartDt; + } + + public void setEffStartDt(Date effStartDt) { + this.effStartDt = effStartDt; + } + + public Date getEffEndDt() { + return effEndDt; + } + + public void setEffEndDt(Date effEndDt) { + this.effEndDt = effEndDt; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/TestCriteriaMultiselectAliasing.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/TestCriteriaMultiselectAliasing.java index 98d33f6a8a..f0c537986b 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/TestCriteriaMultiselectAliasing.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/criteria/multiselect/TestCriteriaMultiselectAliasing.java @@ -1,166 +1,166 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.criteria.multiselect; - -import java.util.ArrayList; -import java.util.List; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.Tuple; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.Expression; -import jakarta.persistence.criteria.Predicate; -import jakarta.persistence.criteria.Root; -import jakarta.persistence.criteria.Selection; -import jakarta.persistence.criteria.Subquery; - -import org.apache.openjpa.persistence.test.SQLListenerTestCase; - -public class TestCriteriaMultiselectAliasing extends SQLListenerTestCase { - CriteriaQuery critQuery; - String critQueryString; - - String doCB = System.getProperty("doCB"); - - @Override - public void setUp() throws Exception { - // Only run on Oracle....the asserts at this time look for SQL specific to the - // way things are generated for Oracle. - setSupportedDatabases( - org.apache.openjpa.jdbc.sql.OracleDictionary.class); - if (isTestsDisabled()) { - return; - } - - super.setUp(DimDay.class, FactWorkAssignment.class - ,"openjpa.Log","SQL=TRACE,Tests=TRACE", "openjpa.ConnectionFactoryProperties", - "PrintParameters=true, PrettyPrint=true, PrettyPrintLineLength=72" - ); - - critQuery = createCriteriaBuilder(); - critQueryString = critQuery.toString(); - System.out.println("critQueryString = " + critQueryString); - } - - public void test (){ - if ("true".equals(doCB)){ - this.ttestCriteriaQuery(); - } - else{ - this.ttestGeneratedCriteriaQueryString(); - } - - } - /** - * This method produce wrong query like (note the extra T_DIM_DAY t3): - * SELECT t0.empl_cnt FROM - * T_FACT_WORK_ASGNMT t0, T_DIM_DAY t1, T_DIM_DAY t3 - * WHERE (t0.CLNT_OBJ_ID = ? AND t1.ROLL_13_MNTH_IND = ? AND t0.pers_obj_id IN ( - * SELECT t2.pers_obj_id FROM T_FACT_WORK_ASGNMT t2 WHERE (t2.CLNT_OBJ_ID = ? AND - * t3.MNTH_STRT_DAY_KY >= ?))) - * [params=(String) dummy1, (int) 1, (String) dummy1, (long) 20150201] - * - * The correct query should be: - * SELECT t0.empl_cnt FROM - * T_FACT_WORK_ASGNMT t0, T_DIM_DAY t1 - * WHERE (t0.CLNT_OBJ_ID = ? AND t1.ROLL_13_MNTH_IND = ? AND t0.pers_obj_id IN ( - * SELECT t2.pers_obj_id FROM T_FACT_WORK_ASGNMT t2 WHERE (t2.CLNT_OBJ_ID = ? AND - * t3.MNTH_STRT_DAY_KY >= ?))) - * [params=(String) dummy1, (int) 1, (String) dummy1, (long) 20150201] - * - */ - public void ttestCriteriaQuery() { - EntityManager em = emf.createEntityManager(); - resetSQL(); - em.createQuery(critQuery).getResultList(); - assertNotSQL(".*T_DIM_DAY t3.*"); - assertSQL(".*T_DIM_DAY t1.*"); - em.close(); - } - - /** - * If we execute just the string generated by Criteria Builder, we - * do not see an extra alias. We see: - * SELECT t0.empl_cnt FROM T_FACT_WORK_ASGNMT t0, T_DIM_DAY t1 - * WHERE (t0.CLNT_OBJ_ID = ? AND t1.ROLL_13_MNTH_IND = ? AND - * t0.pers_obj_id IN (SELECT t2.pers_obj_id FROM - * T_FACT_WORK_ASGNMT t2 WHERE (t2.CLNT_OBJ_ID = ? AND - * t1.MNTH_STRT_DAY_KY = ?))) - */ - public void ttestGeneratedCriteriaQueryString(){ - if (!"true".equals(doCB)){ - EntityManager em = emf.createEntityManager(); - System.out.println("NOT doing CB"); - resetSQL(); - em.createQuery(critQueryString).getResultList(); - assertNotSQL(".*T_DIM_DAY t3.*"); - assertSQL(".*T_DIM_DAY t1.*"); - em.close(); - } - } - - public CriteriaQuery createCriteriaBuilder(){ - EntityManager em = emf.createEntityManager(); - - List predicates = new ArrayList<>(); - CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cq = cb.createTupleQuery(); - Root day = cq.from(DimDay.class); - - Root wa = cq.from(FactWorkAssignment.class); - - predicates.add(cb.equal(wa.get(FactWorkAssignment_.orgOID), "dummy1")); - predicates.add(cb.equal(day.get(DimDay_.roll13MonthInd), 1)); - - Subquery subQuery = cq.subquery(String.class); - - Root wa1 = subQuery.from(FactWorkAssignment.class); - - subQuery.select(wa1.get(FactWorkAssignment_.personObjId)); - List subQueryPredicates = new ArrayList<>(); - subQueryPredicates.add(cb.equal(wa1.get(FactWorkAssignment_.orgOID), "dummy1")); - - //Removing this seem to "fix" the issue....I think the fact that we use 'day' from - //the 'outer' query has an affect....is it OK to use 'day' from the outer query?? I'm - //assuming so since 'testGeneratedCriteriaQueryString' generates the expected SQL. -// subQueryPredicates.add(cb.greaterThanOrEqualTo(day.get(DimDay_.monthStrtDate), new Long(20150201L))); - subQueryPredicates.add(cb.equal(day.get(DimDay_.monthStrtDate), 20150201L)); - - //Doing this places the 'T_DIM_DAY t3' in the 'inner'/sub query. Is this the proper solution?? Or just a - //hacky work around? - //Root day2 = subQuery.from(DimDay.class); - //subQueryPredicates.add(cb.greaterThanOrEqualTo(day2.get(DimDay_.monthStrtDate), new Long(20150201L))); - - subQuery.where(subQueryPredicates.toArray(new Predicate[] {})); - - Predicate predicate = wa.get(FactWorkAssignment_.personObjId).in(subQuery); - - predicates.add(predicate); - - List> selections = new ArrayList<>(); - - Expression expHC = wa.get(FactWorkAssignment_.employeeCount); - selections.add(expHC); - - cq.multiselect(selections).where(predicates.toArray(new Predicate[] {})); - - return cq; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.criteria.multiselect; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.Selection; +import jakarta.persistence.criteria.Subquery; + +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +public class TestCriteriaMultiselectAliasing extends SQLListenerTestCase { + CriteriaQuery critQuery; + String critQueryString; + + String doCB = System.getProperty("doCB"); + + @Override + public void setUp() throws Exception { + // Only run on Oracle....the asserts at this time look for SQL specific to the + // way things are generated for Oracle. + setSupportedDatabases( + org.apache.openjpa.jdbc.sql.OracleDictionary.class); + if (isTestsDisabled()) { + return; + } + + super.setUp(DimDay.class, FactWorkAssignment.class + ,"openjpa.Log","SQL=TRACE,Tests=TRACE", "openjpa.ConnectionFactoryProperties", + "PrintParameters=true, PrettyPrint=true, PrettyPrintLineLength=72" + ); + + critQuery = createCriteriaBuilder(); + critQueryString = critQuery.toString(); + System.out.println("critQueryString = " + critQueryString); + } + + public void test (){ + if ("true".equals(doCB)){ + this.ttestCriteriaQuery(); + } + else{ + this.ttestGeneratedCriteriaQueryString(); + } + + } + /** + * This method produce wrong query like (note the extra T_DIM_DAY t3): + * SELECT t0.empl_cnt FROM + * T_FACT_WORK_ASGNMT t0, T_DIM_DAY t1, T_DIM_DAY t3 + * WHERE (t0.CLNT_OBJ_ID = ? AND t1.ROLL_13_MNTH_IND = ? AND t0.pers_obj_id IN ( + * SELECT t2.pers_obj_id FROM T_FACT_WORK_ASGNMT t2 WHERE (t2.CLNT_OBJ_ID = ? AND + * t3.MNTH_STRT_DAY_KY >= ?))) + * [params=(String) dummy1, (int) 1, (String) dummy1, (long) 20150201] + * + * The correct query should be: + * SELECT t0.empl_cnt FROM + * T_FACT_WORK_ASGNMT t0, T_DIM_DAY t1 + * WHERE (t0.CLNT_OBJ_ID = ? AND t1.ROLL_13_MNTH_IND = ? AND t0.pers_obj_id IN ( + * SELECT t2.pers_obj_id FROM T_FACT_WORK_ASGNMT t2 WHERE (t2.CLNT_OBJ_ID = ? AND + * t3.MNTH_STRT_DAY_KY >= ?))) + * [params=(String) dummy1, (int) 1, (String) dummy1, (long) 20150201] + * + */ + public void ttestCriteriaQuery() { + EntityManager em = emf.createEntityManager(); + resetSQL(); + em.createQuery(critQuery).getResultList(); + assertNotSQL(".*T_DIM_DAY t3.*"); + assertSQL(".*T_DIM_DAY t1.*"); + em.close(); + } + + /** + * If we execute just the string generated by Criteria Builder, we + * do not see an extra alias. We see: + * SELECT t0.empl_cnt FROM T_FACT_WORK_ASGNMT t0, T_DIM_DAY t1 + * WHERE (t0.CLNT_OBJ_ID = ? AND t1.ROLL_13_MNTH_IND = ? AND + * t0.pers_obj_id IN (SELECT t2.pers_obj_id FROM + * T_FACT_WORK_ASGNMT t2 WHERE (t2.CLNT_OBJ_ID = ? AND + * t1.MNTH_STRT_DAY_KY = ?))) + */ + public void ttestGeneratedCriteriaQueryString(){ + if (!"true".equals(doCB)){ + EntityManager em = emf.createEntityManager(); + System.out.println("NOT doing CB"); + resetSQL(); + em.createQuery(critQueryString).getResultList(); + assertNotSQL(".*T_DIM_DAY t3.*"); + assertSQL(".*T_DIM_DAY t1.*"); + em.close(); + } + } + + public CriteriaQuery createCriteriaBuilder(){ + EntityManager em = emf.createEntityManager(); + + List predicates = new ArrayList<>(); + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createTupleQuery(); + Root day = cq.from(DimDay.class); + + Root wa = cq.from(FactWorkAssignment.class); + + predicates.add(cb.equal(wa.get(FactWorkAssignment_.orgOID), "dummy1")); + predicates.add(cb.equal(day.get(DimDay_.roll13MonthInd), 1)); + + Subquery subQuery = cq.subquery(String.class); + + Root wa1 = subQuery.from(FactWorkAssignment.class); + + subQuery.select(wa1.get(FactWorkAssignment_.personObjId)); + List subQueryPredicates = new ArrayList<>(); + subQueryPredicates.add(cb.equal(wa1.get(FactWorkAssignment_.orgOID), "dummy1")); + + //Removing this seem to "fix" the issue....I think the fact that we use 'day' from + //the 'outer' query has an affect....is it OK to use 'day' from the outer query?? I'm + //assuming so since 'testGeneratedCriteriaQueryString' generates the expected SQL. +// subQueryPredicates.add(cb.greaterThanOrEqualTo(day.get(DimDay_.monthStrtDate), new Long(20150201L))); + subQueryPredicates.add(cb.equal(day.get(DimDay_.monthStrtDate), 20150201L)); + + //Doing this places the 'T_DIM_DAY t3' in the 'inner'/sub query. Is this the proper solution?? Or just a + //hacky work around? + //Root day2 = subQuery.from(DimDay.class); + //subQueryPredicates.add(cb.greaterThanOrEqualTo(day2.get(DimDay_.monthStrtDate), new Long(20150201L))); + + subQuery.where(subQueryPredicates.toArray(new Predicate[] {})); + + Predicate predicate = wa.get(FactWorkAssignment_.personObjId).in(subQuery); + + predicates.add(predicate); + + List> selections = new ArrayList<>(); + + Expression expHC = wa.get(FactWorkAssignment_.employeeCount); + selections.add(expHC); + + cq.multiselect(selections).where(predicates.toArray(new Predicate[] {})); + + return cq; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/SerializingConcurrentQueryCache.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/SerializingConcurrentQueryCache.java index efb19fa41a..269232470f 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/SerializingConcurrentQueryCache.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/SerializingConcurrentQueryCache.java @@ -1,63 +1,63 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.datacache; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import org.apache.openjpa.datacache.ConcurrentQueryCache; -import org.apache.openjpa.datacache.QueryKey; -import org.apache.openjpa.datacache.QueryResult; - -/** - * A QueryCache implementation that serializes all keys / values to simulate having a remote cache. - */ -public class SerializingConcurrentQueryCache extends ConcurrentQueryCache { - private static final long serialVersionUID = 1L; - public static String SERIALIZING_CONCURRENT_QUERY_CACHE = SerializingConcurrentQueryCache.class.getName(); - - @Override - public QueryResult get(QueryKey key) { - return roundtrip(super.get(roundtrip(key))); - } - - @Override - public QueryResult put(QueryKey qk, QueryResult oids) { - roundtrip(qk); - roundtrip(oids); - return roundtrip(super.put(qk, oids)); - } - - @SuppressWarnings("unchecked") - private static T roundtrip(T o) { - try { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(bytes); - out.writeObject(o); - out.flush(); - ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray())); - return (T) in.readObject(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.datacache; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.apache.openjpa.datacache.ConcurrentQueryCache; +import org.apache.openjpa.datacache.QueryKey; +import org.apache.openjpa.datacache.QueryResult; + +/** + * A QueryCache implementation that serializes all keys / values to simulate having a remote cache. + */ +public class SerializingConcurrentQueryCache extends ConcurrentQueryCache { + private static final long serialVersionUID = 1L; + public static String SERIALIZING_CONCURRENT_QUERY_CACHE = SerializingConcurrentQueryCache.class.getName(); + + @Override + public QueryResult get(QueryKey key) { + return roundtrip(super.get(roundtrip(key))); + } + + @Override + public QueryResult put(QueryKey qk, QueryResult oids) { + roundtrip(qk); + roundtrip(oids); + return roundtrip(super.put(qk, oids)); + } + + @SuppressWarnings("unchecked") + private static T roundtrip(T o) { + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bytes); + out.writeObject(o); + out.flush(); + ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray())); + return (T) in.readObject(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/identity/Dependent5.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/identity/Dependent5.java index 169192fc80..725e47abe8 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/identity/Dependent5.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/enhance/identity/Dependent5.java @@ -1,54 +1,54 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.enhance.identity; -import java.io.Serializable; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.IdClass; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinColumns; -import jakarta.persistence.ManyToOne; - -@Entity -@IdClass(DependentId5.class) -public class Dependent5 implements Serializable { - - private static final long serialVersionUID = 1L; - @Id - String name; - - @Id - @JoinColumns({ - @JoinColumn(name="FIRSTNAME", referencedColumnName="FIRSTNAME"), - @JoinColumn(name="LASTNAME", referencedColumnName="LASTNAME") - }) - @ManyToOne - Employee5 emp; - - public Dependent5(String name, Employee5 emp) { - this.name = name; - this.emp = emp; - } - - public Dependent5(DependentId5 dId, Employee5 emp){ - this.name = dId.getName(); - this.emp = emp; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.enhance.identity; +import java.io.Serializable; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinColumns; +import jakarta.persistence.ManyToOne; + +@Entity +@IdClass(DependentId5.class) +public class Dependent5 implements Serializable { + + private static final long serialVersionUID = 1L; + @Id + String name; + + @Id + @JoinColumns({ + @JoinColumn(name="FIRSTNAME", referencedColumnName="FIRSTNAME"), + @JoinColumn(name="LASTNAME", referencedColumnName="LASTNAME") + }) + @ManyToOne + Employee5 emp; + + public Dependent5(String name, Employee5 emp) { + this.name = name; + this.emp = emp; + } + + public Dependent5(DependentId5 dId, Employee5 emp){ + this.name = dId.getName(); + this.emp = emp; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestIsolationFecthHint.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestIsolationFecthHint.java index 0490f2e473..717ad41b49 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestIsolationFecthHint.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/TestIsolationFecthHint.java @@ -1,62 +1,61 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.openjpa.persistence.jdbc; - -import jakarta.persistence.Query; - -import org.apache.openjpa.jdbc.conf.JDBCConfiguration; -import org.apache.openjpa.jdbc.sql.DB2Dictionary; -import org.apache.openjpa.jdbc.sql.DBDictionary; -import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI; -import org.apache.openjpa.persistence.simple.AllFieldTypes; -import org.apache.openjpa.persistence.test.SQLListenerTestCase; - -public class TestIsolationFecthHint extends SQLListenerTestCase{ - - @Override - public void setUp(){ - setUp(AllFieldTypes.class, CLEAR_TABLES); - - } - - public void testFetchPlanIsolationURHint(){ - OpenJPAEntityManagerSPI em = emf.createEntityManager(); - try { - DBDictionary dict = ((JDBCConfiguration) em.getConfiguration()) - .getDBDictionaryInstance(); - if (dict instanceof DB2Dictionary) { - AllFieldTypes allFieldTypes = new AllFieldTypes(); - allFieldTypes.setStringField("testString"); - allFieldTypes.setIntField(2012); - - em.getTransaction().begin(); - em.persist(allFieldTypes); - Query query = em.createQuery("select e from AllFieldTypes e where e.stringField = ?1"); - query.setParameter(1, "testString"); - query.setHint("openjpa.FetchPlan.Isolation", "READ_UNCOMMITTED"); - assertEquals(1, query.getResultList().size()); - assertContainsSQL("FOR READ ONLY WITH UR"); - em.getTransaction().rollback(); - } - } finally { - em.close(); - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.openjpa.persistence.jdbc; + +import jakarta.persistence.Query; + +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.jdbc.sql.DB2Dictionary; +import org.apache.openjpa.jdbc.sql.DBDictionary; +import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI; +import org.apache.openjpa.persistence.simple.AllFieldTypes; +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +public class TestIsolationFecthHint extends SQLListenerTestCase{ + + @Override + public void setUp(){ + setUp(AllFieldTypes.class, CLEAR_TABLES); + } + + public void testFetchPlanIsolationURHint(){ + OpenJPAEntityManagerSPI em = emf.createEntityManager(); + try { + DBDictionary dict = ((JDBCConfiguration) em.getConfiguration()) + .getDBDictionaryInstance(); + if (dict instanceof DB2Dictionary) { + AllFieldTypes allFieldTypes = new AllFieldTypes(); + allFieldTypes.setStringField("testString"); + allFieldTypes.setIntField(2012); + + em.getTransaction().begin(); + em.persist(allFieldTypes); + Query query = em.createQuery("select e from AllFieldTypes e where e.stringField = ?1"); + query.setParameter(1, "testString"); + query.setHint("openjpa.FetchPlan.Isolation", "READ_UNCOMMITTED"); + assertEquals(1, query.getResultList().size()); + assertContainsSQL("FOR READ ONLY WITH UR"); + em.getTransaction().rollback(); + } + } finally { + em.close(); + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/Authority.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/Authority.java index f2e1c16ebd..df21ad0847 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/Authority.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/Authority.java @@ -1,79 +1,79 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jdbc.mapping; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.NamedQueries; -import jakarta.persistence.NamedQuery; -import jakarta.persistence.Table; -import javax.xml.bind.annotation.XmlEnum; -import javax.xml.bind.annotation.XmlType; - -import org.apache.openjpa.persistence.Persistent; -import org.apache.openjpa.persistence.jdbc.Strategy; - -@Entity -@Table(name="authority") -@NamedQueries( { - @NamedQuery(name = "AllIonAuthorities", query = "SELECT x FROM IonAuthority x") -}) -public class Authority { -@Id - @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "ID") - private Integer id; - - @Enumerated( EnumType.STRING ) - @Column(nullable=false, length=128, updatable=true, insertable=true) - @Persistent - @Strategy("org.apache.openjpa.jdbc.meta.strats.EnumValueHandler") - private AuthorityValues authorityName; - - - @XmlType(name = "IonAuthorityValues") - @XmlEnum - public enum AuthorityValues { - - AUTH1, - AUTH2, - } - - public Authority() {} - public Authority(AuthorityValues auth) { - authorityName = auth; - } - - public Integer getId() { - return id; - } - - public void setAuthorityName(AuthorityValues auth) { - authorityName = auth; - } - - public AuthorityValues getAuthorityName() { - return authorityName; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jdbc.mapping; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.Table; +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlType; + +import org.apache.openjpa.persistence.Persistent; +import org.apache.openjpa.persistence.jdbc.Strategy; + +@Entity +@Table(name="authority") +@NamedQueries( { + @NamedQuery(name = "AllIonAuthorities", query = "SELECT x FROM IonAuthority x") +}) +public class Authority { +@Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "ID") + private Integer id; + + @Enumerated( EnumType.STRING ) + @Column(nullable=false, length=128, updatable=true, insertable=true) + @Persistent + @Strategy("org.apache.openjpa.jdbc.meta.strats.EnumValueHandler") + private AuthorityValues authorityName; + + + @XmlType(name = "IonAuthorityValues") + @XmlEnum + public enum AuthorityValues { + + AUTH1, + AUTH2, + } + + public Authority() {} + public Authority(AuthorityValues auth) { + authorityName = auth; + } + + public Integer getId() { + return id; + } + + public void setAuthorityName(AuthorityValues auth) { + authorityName = auth; + } + + public AuthorityValues getAuthorityName() { + return authorityName; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/TestEnumXmlTypeMapping.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/TestEnumXmlTypeMapping.java index 5ab41cc65e..08edd64aa3 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/TestEnumXmlTypeMapping.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/mapping/TestEnumXmlTypeMapping.java @@ -1,39 +1,39 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jdbc.mapping; - -import org.apache.openjpa.jdbc.meta.ClassMapping; -import org.apache.openjpa.jdbc.meta.FieldMapping; -import org.apache.openjpa.jdbc.schema.Column; -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -public class TestEnumXmlTypeMapping extends SingleEMFTestCase { - - @Override - public void setUp() { - setUp(Authority.class, DROP_TABLES); - } - - public void testEnumXmlType() { - ClassMapping mapping = getMapping(Authority.class); - FieldMapping fm = mapping.getFieldMapping("authorityName"); - Column[] cols = fm.getColumns(); - assertFalse(cols[0].isXML()); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jdbc.mapping; + +import org.apache.openjpa.jdbc.meta.ClassMapping; +import org.apache.openjpa.jdbc.meta.FieldMapping; +import org.apache.openjpa.jdbc.schema.Column; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestEnumXmlTypeMapping extends SingleEMFTestCase { + + @Override + public void setUp() { + setUp(Authority.class, DROP_TABLES); + } + + public void testEnumXmlType() { + ClassMapping mapping = getMapping(Authority.class); + FieldMapping fm = mapping.getFieldMapping("authorityName"); + Column[] cols = fm.getColumns(); + assertFalse(cols[0].isXML()); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/meta/TestUseSchemaElement.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/meta/TestUseSchemaElement.java index bcd4e194c6..f318bfcc07 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/meta/TestUseSchemaElement.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/meta/TestUseSchemaElement.java @@ -1,146 +1,146 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jdbc.meta; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.Scanner; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; - -import org.apache.openjpa.jdbc.conf.JDBCConfiguration; -import org.apache.openjpa.jdbc.meta.ReverseMappingTool; -import org.apache.openjpa.lib.util.Files; -import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory; -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -/** - * Tests the added useSchemaElement functionality of the - * ReverseMappingTool and CodeGenerator classes. - * - * @author Austin Dorenkamp (ajdorenk) - */ -public class TestUseSchemaElement extends /*TestCase*/ SingleEMFTestCase { - - @Override - public void setUp() throws Exception { - super.setUp(); - File f = new File("target/orm.xml"); - - // Make sure to clean up orm.xml from a prior run - if (f.exists()) { - assertTrue(f.delete()); - } - setSupportedDatabases(org.apache.openjpa.jdbc.sql.DerbyDictionary.class); - } - - @Override - public String getPersistenceUnitName(){ - return "rev-mapping-pu"; - } - - public void testGettersAndSetters() throws Exception { - - JDBCConfiguration conf = (JDBCConfiguration) ((OpenJPAEntityManagerFactory) emf).getConfiguration(); - - EntityManager em = emf.createEntityManager(); - - em.getTransaction().begin(); - - Query q = em.createNativeQuery("CREATE TABLE USCHEMA.USCHANTBL (ID INTEGER PRIMARY KEY)"); - try { - q.executeUpdate(); - em.getTransaction().commit(); - } catch (Throwable t) { - em.getTransaction().rollback(); - System.out.println(t.toString()); - } - - ReverseMappingTool.Flags flags = new ReverseMappingTool.Flags(); - flags.metaDataLevel = "package"; - flags.generateAnnotations = true; - flags.accessType = "property"; - flags.nullableAsObject = true; - flags.useSchemaName = false; - flags.useSchemaElement = false; - flags.packageName = ""; - flags.directory = Files.getFile("./target", null); - ReverseMappingTool.run(conf, new String[0], flags, null); - - /* Now that the tool has been run, we will test it by reading the generated files */ - - // This tests the removal of the schema annotation in the Uschantbl.java file - File uschantbl = new File("./target/Uschantbl.java"); - Scanner inFile = null; - String currentLine; - try { - inFile = new Scanner(uschantbl); - - while(inFile.hasNextLine()) - { - currentLine = inFile.nextLine(); - if((currentLine.length()) > 0 && (currentLine.charAt(0) != '@')) - { - continue; - } - - if(currentLine.contains("Table(schema=")) - { - fail("Uschantbl.java still contains schema name"); - } - } - - } catch (FileNotFoundException e) { - fail("Uschantbl.java not generated in ./target by ReverseMappingTool"); - } - finally { - if (inFile != null) { - inFile.close(); - } - } - - // Delete file to clean up workspace - assertTrue(uschantbl.delete()); - - // This tests the removal of the schema name from the orm.xml file - File orm = new File("target/orm.xml"); - try { - inFile = new Scanner(orm); - while(inFile.hasNextLine()) - { - if(inFile.nextLine().contains(" 0 && (currentLine.charAt(0) != '@')) + { + continue; + } + + if(currentLine.contains("Table(schema=")) + { + fail("Uschantbl.java still contains schema name"); + } + } + + } catch (FileNotFoundException e) { + fail("Uschantbl.java not generated in ./target by ReverseMappingTool"); + } + finally { + if (inFile != null) { + inFile.close(); + } + } + + // Delete file to clean up workspace + assertTrue(uschantbl.delete()); + + // This tests the removal of the schema name from the orm.xml file + File orm = new File("target/orm.xml"); + try { + inFile = new Scanner(orm); + while(inFile.hasNextLine()) + { + if(inFile.nextLine().contains("
employees; - @OneToMany(mappedBy="dept", cascade=CascadeType.ALL) - private List employee2s; - - public Department() { - super(); - } - - public Department(int deptno, String name) { - super(); - this.deptno = deptno; - this.name = name; - } - - public int getDeptno() { - return this.deptno; - } - - public void setDeptno(int deptno) { - this.deptno = deptno; - } - - public int getVersion() { - return this.version; - } - - public void setVersion(int version) { - this.version = version; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public List getEmployees() { - return this.employees; - } - - public void setEmployees(List employees) { - this.employees = employees; - } - - public List getEmployee2s() { - return this.employee2s; - } - - public void setEmployee2s(List employees) { - this.employee2s = employees; - } - - @Override - public String toString() { - return "[Department:depno=" + deptno + ", version=" + version + ", name=" + name + - ", employees=" + employees + ", employee2s=" + employee2s+ ']'; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.joins; + +import java.io.Serializable; +import java.util.List; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import jakarta.persistence.Version; + +/** + * Entity implementation class for Entity: Parent + * + */ +@Entity +@Table(name="FETCHDEPT") +public class Department implements Serializable { + + private static final long serialVersionUID = -5537435298484817651L; + + @Id + private int deptno; + @Version + private int version; + private String name; + @OneToMany(mappedBy="dept", cascade=CascadeType.ALL) + private List employees; + @OneToMany(mappedBy="dept", cascade=CascadeType.ALL) + private List employee2s; + + public Department() { + super(); + } + + public Department(int deptno, String name) { + super(); + this.deptno = deptno; + this.name = name; + } + + public int getDeptno() { + return this.deptno; + } + + public void setDeptno(int deptno) { + this.deptno = deptno; + } + + public int getVersion() { + return this.version; + } + + public void setVersion(int version) { + this.version = version; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public List getEmployees() { + return this.employees; + } + + public void setEmployees(List employees) { + this.employees = employees; + } + + public List getEmployee2s() { + return this.employee2s; + } + + public void setEmployee2s(List employees) { + this.employee2s = employees; + } + + @Override + public String toString() { + return "[Department:depno=" + deptno + ", version=" + version + ", name=" + name + + ", employees=" + employees + ", employee2s=" + employee2s+ ']'; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee.java index 0569aaa780..a2f03a2528 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/Employee.java @@ -1,98 +1,98 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.joins; - -import java.io.Serializable; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import jakarta.persistence.Version; - -/** - * Entity implementation class for Entity: Child - * - */ -@Entity -@Table(name = "FETCHEMPL") -public class Employee implements Serializable { - - private static final long serialVersionUID = -5155314943010802723L; - - @Id - private int empno; - private String name; - @Version - private int version; - - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) - private Department dept; - - public Employee() { - super(); - } - - public Employee(int empno, String name, Department dept) { - super(); - this.empno = empno; - this.name = name; - this.dept = dept; - } - - public int getEmpno() { - return this.empno; - } - - public void setEmpno(int empno) { - this.empno = empno; - } - - public int getVersion() { - return this.version; - } - - public void setVersion(int version) { - this.version = version; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public Department getDept() { - return dept; - } - - public void setDept(Department dept) { - this.dept = dept; - } - - @Override - public String toString() { - return "[Employee:id=" + empno + ", version=" + version + ", name=" - + name + ']'; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.joins; + +import java.io.Serializable; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.Version; + +/** + * Entity implementation class for Entity: Child + * + */ +@Entity +@Table(name = "FETCHEMPL") +public class Employee implements Serializable { + + private static final long serialVersionUID = -5155314943010802723L; + + @Id + private int empno; + private String name; + @Version + private int version; + + @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + private Department dept; + + public Employee() { + super(); + } + + public Employee(int empno, String name, Department dept) { + super(); + this.empno = empno; + this.name = name; + this.dept = dept; + } + + public int getEmpno() { + return this.empno; + } + + public void setEmpno(int empno) { + this.empno = empno; + } + + public int getVersion() { + return this.version; + } + + public void setVersion(int version) { + this.version = version; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public Department getDept() { + return dept; + } + + public void setDept(Department dept) { + this.dept = dept; + } + + @Override + public String toString() { + return "[Employee:id=" + empno + ", version=" + version + ", name=" + + name + ']'; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/TestJoinFetchWithQueryDataCache.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/TestJoinFetchWithQueryDataCache.java index 7029bf8b58..dbe73e5cfe 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/TestJoinFetchWithQueryDataCache.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/TestJoinFetchWithQueryDataCache.java @@ -1,245 +1,245 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.joins; - -import java.util.ArrayList; -import java.util.List; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.criteria.CriteriaBuilder; -import jakarta.persistence.criteria.CriteriaQuery; -import jakarta.persistence.criteria.JoinType; -import jakarta.persistence.criteria.Root; - -import org.apache.openjpa.persistence.test.SQLListenerTestCase; - -import org.junit.Assert; - -/** - * Tests JQPL and Criteria API equivalent using left join fetch with QueryCache and DataCache enabled. - */ -public class TestJoinFetchWithQueryDataCache extends SQLListenerTestCase { - EntityManager em; - - @Override - public void setUp() { - super.setUp(DROP_TABLES, Employee.class, Department.class, "openjpa.QueryCompilationCache", "all", - "openjpa.DataCache", "true", "openjpa.RemoteCommitProvider", "sjvm", "openjpa.QueryCache", "true" - // This is a hack to work around using em.detach(...) w/ a 1.0 p.xml - , "openjpa.Compatibility", "CopyOnDetach=false" - ); - - em = emf.createEntityManager(); - em.getTransaction().begin(); - - Department dept; - dept = new Department(10, "department 10"); - dept.setEmployees(new ArrayList<>()); - dept.getEmployees().add(new Employee(11, "Emp11", dept)); - dept.getEmployees().add(new Employee(12, "Emp12", dept)); - dept.setEmployee2s(new ArrayList<>()); - dept.getEmployee2s().add(new Employee(211, "Emp211", dept)); - dept.getEmployee2s().add(new Employee(212, "Emp212", dept)); - em.persist(dept); - - dept = new Department(20, "department 20"); - dept.setEmployees(new ArrayList<>()); - dept.getEmployees().add(new Employee(21, "Emp21", dept)); - dept.getEmployees().add(new Employee(22, "Emp22", dept)); - dept.setEmployee2s(new ArrayList<>()); - dept.getEmployee2s().add(new Employee(221, "Emp221", dept)); - dept.getEmployee2s().add(new Employee(222, "Emp222", dept)); - em.persist(dept); - - em.getTransaction().commit(); - - em.close(); - } - - public void testJPQLNoFetch() { - EntityManager em = emf.createEntityManager(); - List ds = em.createQuery("SELECT DISTINCT d FROM Department d WHERE d.deptno = 10").getResultList(); - System.out.println("-- testJPQLNoFetch -----"); - em.clear(); - Assert.assertEquals(1, ds.size()); - for (Department x : ds) { - Assert.assertNull(x.getEmployees()); - Assert.assertNull(x.getEmployee2s()); - System.out.println(x); - } - - em.close(); - } - - public void testJPQLOneFetch() { - EntityManager em = emf.createEntityManager(); - List ds = - em.createQuery("SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.employee2s " + "WHERE d.deptno = 10") - .getResultList(); - System.out.println("-- testJPQLOneFetch -----"); - em.clear(); - Assert.assertEquals(1, ds.size()); - for (Department x : ds) { - Assert.assertNull(x.getEmployees()); - Assert.assertNotNull(x.getEmployee2s()); - Assert.assertEquals(2, x.getEmployee2s().size()); - System.out.println(x); - } - - em.close(); - } - - public void testJPQLTwoFetch() { - EntityManager em = emf.createEntityManager(); - List ds = - em.createQuery( - "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.employees " + "LEFT JOIN FETCH d.employee2s " - + "WHERE d.deptno = 10").getResultList(); - System.out.println("-- testJPQLTwoFetch -----"); - em.clear(); - Assert.assertEquals(1, ds.size()); - for (Department x : ds) { - Assert.assertNotNull(x.getEmployees()); - Assert.assertEquals(2, x.getEmployees().size()); - Assert.assertNotNull(x.getEmployee2s()); - Assert.assertEquals(2, x.getEmployee2s().size()); - System.out.println(x); - } - - em.close(); - } - - public void testCriteriaAPINoFetch() { - EntityManager em = emf.createEntityManager(); - CriteriaBuilder cb = em.getCriteriaBuilder(); - - // This query is equivalent to the following Java Persistence query - // language query: - // SELECT d - // FROM Department d - // WHERE d.deptno = 1 - - CriteriaQuery q = cb.createQuery(Department.class); - Root d = q.from(Department.class); - q.where(cb.equal(d.get(Department_.deptno), 20)).select(d); - - List ds = em.createQuery(q).getResultList(); - System.out.println("-- testCriteriaAPINoFetch -----"); - em.clear(); - Assert.assertEquals(1, ds.size()); - for (Department x : ds) { - Assert.assertNull(x.getEmployees()); - Assert.assertNull(x.getEmployee2s()); - System.out.println(x); - } - - em.close(); - } - - public void testCriteriaAPIOneFetch() { - EntityManager em = emf.createEntityManager(); - CriteriaBuilder cb = em.getCriteriaBuilder(); - - // 6.5.4 Fetch Joins - // Example: - // CriteriaQuery q = cb.createQuery(Department.class); - // Root d = q.from(Department.class); - // d.fetch(Department_.employees, JoinType.LEFT); - // q.where(cb.equal(d.get(Department_.deptno), 1)).select(d); - // - // This query is equivalent to the following Java Persistence query - // language query: - // SELECT DISTINCT d - // FROM Department d LEFT JOIN FETCH d.employees - // WHERE d.deptno = 1 - - CriteriaQuery q = cb.createQuery(Department.class); - Root d = q.from(Department.class); - d.fetch(Department_.employees, JoinType.LEFT); - q.where(cb.equal(d.get(Department_.deptno), 20)).select(d).distinct(true); - - List ds = em.createQuery(q).getResultList(); - System.out.println("-- testCriteriaAPIOneFetch -----"); - em.clear(); - Assert.assertEquals(1, ds.size()); - for (Department x : ds) { - Assert.assertNotNull(x.getEmployees()); - Assert.assertEquals(2, x.getEmployees().size()); - Assert.assertNull(x.getEmployee2s()); - System.out.println(x); - } - - em.close(); - } - - public void testCriteriaAPITwoFetch() { - EntityManager em = emf.createEntityManager(); - CriteriaBuilder cb = em.getCriteriaBuilder(); - - // This query is equivalent to the following Java Persistence query - // language query: - // SELECT DISTINCT d - // FROM Department d LEFT JOIN FETCH d.employees LEFT JOIN FETCH d.employee2s - // WHERE d.deptno = 1 - CriteriaQuery q = cb.createQuery(Department.class); - Root d = q.from(Department.class); - d.fetch(Department_.employees, JoinType.LEFT); - d.fetch(Department_.employee2s, JoinType.LEFT); - q.where(cb.equal(d.get(Department_.deptno), 20)).select(d).distinct(true); - - List ds = em.createQuery(q).getResultList(); - System.out.println("-- testCriteriaAPITwoFetch -----"); - em.clear(); - Assert.assertEquals(1, ds.size()); - for (Department x : ds) { - Assert.assertNotNull(x.getEmployees()); - Assert.assertEquals(2, x.getEmployees().size()); - Assert.assertNotNull(x.getEmployee2s()); - Assert.assertEquals(2, x.getEmployee2s().size()); - System.out.println(x); - } - - em.close(); - } - - public void testConsecutiveJPQLJoinFetchCall() { - doQuery(emf, false); - doQuery(emf, true); - } - - private void doQuery(EntityManagerFactory emf, boolean cached) { - String query = "select o from Employee o " + "left join fetch o.dept " + "where o.dept.deptno = 10"; - EntityManager em = emf.createEntityManager(); - - sql.clear(); - List emps = em.createQuery(query, Employee.class).getResultList(); - Assert.assertEquals(4, emps.size()); - for (Employee emp : emps) { - em.detach(emp); - - Assert.assertNotNull(emp.getDept()); - Assert.assertEquals(2, emp.getDept().getEmployees().size()); - } - em.close(); - if (cached) { - assertTrue(sql.size() == 0); - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.joins; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Root; + +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +import org.junit.Assert; + +/** + * Tests JQPL and Criteria API equivalent using left join fetch with QueryCache and DataCache enabled. + */ +public class TestJoinFetchWithQueryDataCache extends SQLListenerTestCase { + EntityManager em; + + @Override + public void setUp() { + super.setUp(DROP_TABLES, Employee.class, Department.class, "openjpa.QueryCompilationCache", "all", + "openjpa.DataCache", "true", "openjpa.RemoteCommitProvider", "sjvm", "openjpa.QueryCache", "true" + // This is a hack to work around using em.detach(...) w/ a 1.0 p.xml + , "openjpa.Compatibility", "CopyOnDetach=false" + ); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + + Department dept; + dept = new Department(10, "department 10"); + dept.setEmployees(new ArrayList<>()); + dept.getEmployees().add(new Employee(11, "Emp11", dept)); + dept.getEmployees().add(new Employee(12, "Emp12", dept)); + dept.setEmployee2s(new ArrayList<>()); + dept.getEmployee2s().add(new Employee(211, "Emp211", dept)); + dept.getEmployee2s().add(new Employee(212, "Emp212", dept)); + em.persist(dept); + + dept = new Department(20, "department 20"); + dept.setEmployees(new ArrayList<>()); + dept.getEmployees().add(new Employee(21, "Emp21", dept)); + dept.getEmployees().add(new Employee(22, "Emp22", dept)); + dept.setEmployee2s(new ArrayList<>()); + dept.getEmployee2s().add(new Employee(221, "Emp221", dept)); + dept.getEmployee2s().add(new Employee(222, "Emp222", dept)); + em.persist(dept); + + em.getTransaction().commit(); + + em.close(); + } + + public void testJPQLNoFetch() { + EntityManager em = emf.createEntityManager(); + List ds = em.createQuery("SELECT DISTINCT d FROM Department d WHERE d.deptno = 10").getResultList(); + System.out.println("-- testJPQLNoFetch -----"); + em.clear(); + Assert.assertEquals(1, ds.size()); + for (Department x : ds) { + Assert.assertNull(x.getEmployees()); + Assert.assertNull(x.getEmployee2s()); + System.out.println(x); + } + + em.close(); + } + + public void testJPQLOneFetch() { + EntityManager em = emf.createEntityManager(); + List ds = + em.createQuery("SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.employee2s " + "WHERE d.deptno = 10") + .getResultList(); + System.out.println("-- testJPQLOneFetch -----"); + em.clear(); + Assert.assertEquals(1, ds.size()); + for (Department x : ds) { + Assert.assertNull(x.getEmployees()); + Assert.assertNotNull(x.getEmployee2s()); + Assert.assertEquals(2, x.getEmployee2s().size()); + System.out.println(x); + } + + em.close(); + } + + public void testJPQLTwoFetch() { + EntityManager em = emf.createEntityManager(); + List ds = + em.createQuery( + "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.employees " + "LEFT JOIN FETCH d.employee2s " + + "WHERE d.deptno = 10").getResultList(); + System.out.println("-- testJPQLTwoFetch -----"); + em.clear(); + Assert.assertEquals(1, ds.size()); + for (Department x : ds) { + Assert.assertNotNull(x.getEmployees()); + Assert.assertEquals(2, x.getEmployees().size()); + Assert.assertNotNull(x.getEmployee2s()); + Assert.assertEquals(2, x.getEmployee2s().size()); + System.out.println(x); + } + + em.close(); + } + + public void testCriteriaAPINoFetch() { + EntityManager em = emf.createEntityManager(); + CriteriaBuilder cb = em.getCriteriaBuilder(); + + // This query is equivalent to the following Java Persistence query + // language query: + // SELECT d + // FROM Department d + // WHERE d.deptno = 1 + + CriteriaQuery q = cb.createQuery(Department.class); + Root d = q.from(Department.class); + q.where(cb.equal(d.get(Department_.deptno), 20)).select(d); + + List ds = em.createQuery(q).getResultList(); + System.out.println("-- testCriteriaAPINoFetch -----"); + em.clear(); + Assert.assertEquals(1, ds.size()); + for (Department x : ds) { + Assert.assertNull(x.getEmployees()); + Assert.assertNull(x.getEmployee2s()); + System.out.println(x); + } + + em.close(); + } + + public void testCriteriaAPIOneFetch() { + EntityManager em = emf.createEntityManager(); + CriteriaBuilder cb = em.getCriteriaBuilder(); + + // 6.5.4 Fetch Joins + // Example: + // CriteriaQuery q = cb.createQuery(Department.class); + // Root d = q.from(Department.class); + // d.fetch(Department_.employees, JoinType.LEFT); + // q.where(cb.equal(d.get(Department_.deptno), 1)).select(d); + // + // This query is equivalent to the following Java Persistence query + // language query: + // SELECT DISTINCT d + // FROM Department d LEFT JOIN FETCH d.employees + // WHERE d.deptno = 1 + + CriteriaQuery q = cb.createQuery(Department.class); + Root d = q.from(Department.class); + d.fetch(Department_.employees, JoinType.LEFT); + q.where(cb.equal(d.get(Department_.deptno), 20)).select(d).distinct(true); + + List ds = em.createQuery(q).getResultList(); + System.out.println("-- testCriteriaAPIOneFetch -----"); + em.clear(); + Assert.assertEquals(1, ds.size()); + for (Department x : ds) { + Assert.assertNotNull(x.getEmployees()); + Assert.assertEquals(2, x.getEmployees().size()); + Assert.assertNull(x.getEmployee2s()); + System.out.println(x); + } + + em.close(); + } + + public void testCriteriaAPITwoFetch() { + EntityManager em = emf.createEntityManager(); + CriteriaBuilder cb = em.getCriteriaBuilder(); + + // This query is equivalent to the following Java Persistence query + // language query: + // SELECT DISTINCT d + // FROM Department d LEFT JOIN FETCH d.employees LEFT JOIN FETCH d.employee2s + // WHERE d.deptno = 1 + CriteriaQuery q = cb.createQuery(Department.class); + Root d = q.from(Department.class); + d.fetch(Department_.employees, JoinType.LEFT); + d.fetch(Department_.employee2s, JoinType.LEFT); + q.where(cb.equal(d.get(Department_.deptno), 20)).select(d).distinct(true); + + List ds = em.createQuery(q).getResultList(); + System.out.println("-- testCriteriaAPITwoFetch -----"); + em.clear(); + Assert.assertEquals(1, ds.size()); + for (Department x : ds) { + Assert.assertNotNull(x.getEmployees()); + Assert.assertEquals(2, x.getEmployees().size()); + Assert.assertNotNull(x.getEmployee2s()); + Assert.assertEquals(2, x.getEmployee2s().size()); + System.out.println(x); + } + + em.close(); + } + + public void testConsecutiveJPQLJoinFetchCall() { + doQuery(emf, false); + doQuery(emf, true); + } + + private void doQuery(EntityManagerFactory emf, boolean cached) { + String query = "select o from Employee o " + "left join fetch o.dept " + "where o.dept.deptno = 10"; + EntityManager em = emf.createEntityManager(); + + sql.clear(); + List emps = em.createQuery(query, Employee.class).getResultList(); + Assert.assertEquals(4, emps.size()); + for (Employee emp : emps) { + em.detach(emp); + + Assert.assertNotNull(emp.getDept()); + Assert.assertEquals(2, emp.getDept().getEmployees().size()); + } + em.close(); + if (cached) { + assertTrue(sql.size() == 0); + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/DepartmentTest.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/DepartmentTest.java index be69a4f5b1..9512388749 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/DepartmentTest.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/DepartmentTest.java @@ -1,64 +1,64 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.joins.leftfetch; - -import java.util.HashSet; -import java.util.Set; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import jakarta.persistence.OrderBy; - -@Entity -public class DepartmentTest{ - - @Id - private String primaryKey; - - @OrderBy("name") - @OneToMany(mappedBy = "departmentTest") - private Set persons = new HashSet<>(); - - private String name; - - public Set getPersons() { - return persons; - } - - public void setPersons(Set persons) { - this.persons = persons; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPrimaryKey() { - return this.primaryKey; - } - - public void setPrimaryKey(String primaryKey) { - this.primaryKey = primaryKey; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.joins.leftfetch; + +import java.util.HashSet; +import java.util.Set; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OrderBy; + +@Entity +public class DepartmentTest{ + + @Id + private String primaryKey; + + @OrderBy("name") + @OneToMany(mappedBy = "departmentTest") + private Set persons = new HashSet<>(); + + private String name; + + public Set getPersons() { + return persons; + } + + public void setPersons(Set persons) { + this.persons = persons; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPrimaryKey() { + return this.primaryKey; + } + + public void setPrimaryKey(String primaryKey) { + this.primaryKey = primaryKey; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/PersonTest.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/PersonTest.java index 89643f69be..f9670d27e7 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/PersonTest.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/PersonTest.java @@ -1,70 +1,70 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.joins.leftfetch; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; - -import org.apache.openjpa.persistence.jdbc.ForeignKey; - -@Entity -public class PersonTest { - - @Id - private String primaryKey; - - @ManyToOne - @ForeignKey - private DepartmentTest departmentTest; - - private String name; - - public DepartmentTest getDepartmentTest() { - return departmentTest; - } - - public void setDepartmentTest(DepartmentTest departmentTest) { - this.departmentTest = departmentTest; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getPrimaryKey() { - return this.primaryKey; - } - - public void setPrimaryKey(String primaryKey) { - this.primaryKey = primaryKey; - } - - @Override - public String toString() - { - final StringBuilder sb = new StringBuilder(); - sb.append(this.getName()).append(" - ").append(this.getPrimaryKey()).append(" "); - return sb.toString(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.joins.leftfetch; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; + +import org.apache.openjpa.persistence.jdbc.ForeignKey; + +@Entity +public class PersonTest { + + @Id + private String primaryKey; + + @ManyToOne + @ForeignKey + private DepartmentTest departmentTest; + + private String name; + + public DepartmentTest getDepartmentTest() { + return departmentTest; + } + + public void setDepartmentTest(DepartmentTest departmentTest) { + this.departmentTest = departmentTest; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPrimaryKey() { + return this.primaryKey; + } + + public void setPrimaryKey(String primaryKey) { + this.primaryKey = primaryKey; + } + + @Override + public String toString() + { + final StringBuilder sb = new StringBuilder(); + sb.append(this.getName()).append(" - ").append(this.getPrimaryKey()).append(" "); + return sb.toString(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/TestJoinLeftFetch.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/TestJoinLeftFetch.java index ca94f05ca2..6cfd2beeaa 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/TestJoinLeftFetch.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/joins/leftfetch/TestJoinLeftFetch.java @@ -1,170 +1,170 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.joins.leftfetch; - -import java.util.List; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; - -import org.apache.openjpa.persistence.OpenJPAPersistence; -import org.apache.openjpa.persistence.OpenJPAQuery; -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -public class TestJoinLeftFetch extends SingleEMFTestCase { - - @Override - public void setUp() { - setUp(DROP_TABLES, DepartmentTest.class, PersonTest.class); - createTestData(); - } - - /* - * This test fails (prior to OJ-2475) because the - * DepartmentTests are not populated with the correct - * number of PersonTests - */ - public void testReadDepartmentsWithLeftJoinFetch() { - - EntityManager em = emf.createEntityManager(); - - String qStrDIST = "SELECT DISTINCT dept FROM DepartmentTest " - + "dept LEFT JOIN FETCH dept.persons"; - - Query query = em.createQuery(qStrDIST); - List depts = query.getResultList(); - verifySize(depts); - em.close(); - } - - public void verifySize(List depts){ - for (DepartmentTest department : depts) { - if (department.getPrimaryKey().equals("001")) { -// System.out.println("Dept: " + department.getName()); - // Iterator i = department.getPersons().iterator(); - // while (i.hasNext()){ - // System.out.println("i.next() = " + i.next()); - // } - assertEquals("Size should be 3", 3, department.getPersons().size()); - } - if (department.getPrimaryKey().equals("002")) { -// System.out.println("Dept: " + department.getName()); - // Iterator i = department.getPersons().iterator(); - // while (i.hasNext()){ - // System.out.println("i.next() = " + i.next()); - // } - assertEquals("Size should be 2", 2, department.getPersons().size()); - } - } - } - - /* - * This test works as expected. - */ - public void testReadDepartmentsWithFetchPlan() { - - EntityManager em = emf.createEntityManager(); - - OpenJPAQuery query = OpenJPAPersistence.cast(em.createQuery(" SELECT dept FROM " - + " DepartmentTest dept ")); - query.getFetchPlan().addField(DepartmentTest.class, "persons"); - - verifySize(query.getResultList()); - - em.close(); - } - - /* - * This test works as expected. - */ - public void testReadDepartmentsWithLeftJoinFetchAndOrderBy() { - - EntityManager em = emf.createEntityManager(); - - Query query = em.createQuery(" SELECT dept FROM " + " DepartmentTest dept " - + " LEFT JOIN FETCH dept.persons ORDER BY dept.primaryKey"); - verifySize(query.getResultList()); - - em.close(); - } - - public void createTestData() { - // NOTE: This test depends upon the the PersonTest - // to be un-ordered w.r.t the DepartmentTest FK. - // I've executed a flush after each entity creation - // in an attempt that the FKs will not be ordered. - // @OrderBy is used in the DepartmentTest in order - // to ensure things aren't orderd by the FK. - - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - - DepartmentTest dt1 = new DepartmentTest(); - dt1.setPrimaryKey("001"); - dt1.setName("Dept001"); - em.persist(dt1); - - DepartmentTest dt2 = new DepartmentTest(); - dt2.setPrimaryKey("002"); - dt2.setName("Dept002"); - em.persist(dt2); - - PersonTest pt = new PersonTest(); - pt.setPrimaryKey("1"); - pt.setName("John"); - pt.setDepartmentTest(dt1); - em.persist(pt); - em.flush(); - - pt = new PersonTest(); - pt.setPrimaryKey("2"); - pt.setName("Mark"); - pt.setDepartmentTest(dt1); - em.persist(pt); - em.flush(); - - pt = new PersonTest(); - pt.setPrimaryKey("3"); - pt.setName("Stuart"); - pt.setDepartmentTest(dt2); - em.persist(pt); - em.flush(); - - pt = new PersonTest(); - pt.setPrimaryKey("4"); - pt.setName("Jim"); - pt.setDepartmentTest(dt1); - em.persist(pt); - em.flush(); - - pt = new PersonTest(); - pt.setPrimaryKey("5"); - pt.setName("Fred"); - pt.setDepartmentTest(dt2); - em.persist(pt); - em.flush(); - - em.getTransaction().commit(); - em.close(); - } -} - - - - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.joins.leftfetch; + +import java.util.List; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; + +import org.apache.openjpa.persistence.OpenJPAPersistence; +import org.apache.openjpa.persistence.OpenJPAQuery; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestJoinLeftFetch extends SingleEMFTestCase { + + @Override + public void setUp() { + setUp(DROP_TABLES, DepartmentTest.class, PersonTest.class); + createTestData(); + } + + /* + * This test fails (prior to OJ-2475) because the + * DepartmentTests are not populated with the correct + * number of PersonTests + */ + public void testReadDepartmentsWithLeftJoinFetch() { + + EntityManager em = emf.createEntityManager(); + + String qStrDIST = "SELECT DISTINCT dept FROM DepartmentTest " + + "dept LEFT JOIN FETCH dept.persons"; + + Query query = em.createQuery(qStrDIST); + List depts = query.getResultList(); + verifySize(depts); + em.close(); + } + + public void verifySize(List depts){ + for (DepartmentTest department : depts) { + if (department.getPrimaryKey().equals("001")) { +// System.out.println("Dept: " + department.getName()); + // Iterator i = department.getPersons().iterator(); + // while (i.hasNext()){ + // System.out.println("i.next() = " + i.next()); + // } + assertEquals("Size should be 3", 3, department.getPersons().size()); + } + if (department.getPrimaryKey().equals("002")) { +// System.out.println("Dept: " + department.getName()); + // Iterator i = department.getPersons().iterator(); + // while (i.hasNext()){ + // System.out.println("i.next() = " + i.next()); + // } + assertEquals("Size should be 2", 2, department.getPersons().size()); + } + } + } + + /* + * This test works as expected. + */ + public void testReadDepartmentsWithFetchPlan() { + + EntityManager em = emf.createEntityManager(); + + OpenJPAQuery query = OpenJPAPersistence.cast(em.createQuery(" SELECT dept FROM " + + " DepartmentTest dept ")); + query.getFetchPlan().addField(DepartmentTest.class, "persons"); + + verifySize(query.getResultList()); + + em.close(); + } + + /* + * This test works as expected. + */ + public void testReadDepartmentsWithLeftJoinFetchAndOrderBy() { + + EntityManager em = emf.createEntityManager(); + + Query query = em.createQuery(" SELECT dept FROM " + " DepartmentTest dept " + + " LEFT JOIN FETCH dept.persons ORDER BY dept.primaryKey"); + verifySize(query.getResultList()); + + em.close(); + } + + public void createTestData() { + // NOTE: This test depends upon the the PersonTest + // to be un-ordered w.r.t the DepartmentTest FK. + // I've executed a flush after each entity creation + // in an attempt that the FKs will not be ordered. + // @OrderBy is used in the DepartmentTest in order + // to ensure things aren't orderd by the FK. + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + DepartmentTest dt1 = new DepartmentTest(); + dt1.setPrimaryKey("001"); + dt1.setName("Dept001"); + em.persist(dt1); + + DepartmentTest dt2 = new DepartmentTest(); + dt2.setPrimaryKey("002"); + dt2.setName("Dept002"); + em.persist(dt2); + + PersonTest pt = new PersonTest(); + pt.setPrimaryKey("1"); + pt.setName("John"); + pt.setDepartmentTest(dt1); + em.persist(pt); + em.flush(); + + pt = new PersonTest(); + pt.setPrimaryKey("2"); + pt.setName("Mark"); + pt.setDepartmentTest(dt1); + em.persist(pt); + em.flush(); + + pt = new PersonTest(); + pt.setPrimaryKey("3"); + pt.setName("Stuart"); + pt.setDepartmentTest(dt2); + em.persist(pt); + em.flush(); + + pt = new PersonTest(); + pt.setPrimaryKey("4"); + pt.setName("Jim"); + pt.setDepartmentTest(dt1); + em.persist(pt); + em.flush(); + + pt = new PersonTest(); + pt.setPrimaryKey("5"); + pt.setName("Fred"); + pt.setDepartmentTest(dt2); + em.persist(pt); + em.flush(); + + em.getTransaction().commit(); + em.close(); + } +} + + + + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/literals/TestLiteralInSQL.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/literals/TestLiteralInSQL.java index 67f236e132..08239d53f7 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/literals/TestLiteralInSQL.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/literals/TestLiteralInSQL.java @@ -1,64 +1,64 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.literals; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; - -import org.apache.openjpa.jdbc.conf.JDBCConfiguration; -import org.apache.openjpa.jdbc.sql.DBDictionary; -import org.apache.openjpa.jdbc.sql.H2Dictionary; -import org.apache.openjpa.jdbc.sql.PostgresDictionary; -import org.apache.openjpa.persistence.simple.AllFieldTypes; -import org.apache.openjpa.persistence.test.SQLListenerTestCase; - -public class TestLiteralInSQL extends SQLListenerTestCase { - @Override - public void setUp() { - setUp(AllFieldTypes.class, "openjpa.jdbc.QuerySQLCache", "false"); - } - - public void testTrueInSQL() { - EntityManager em = emf.createEntityManager(); - - em = emf.createEntityManager(); - DBDictionary dict = ((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance(); - //Disable on Postgres for now.... - if (dict instanceof PostgresDictionary || (dict instanceof H2Dictionary && dict.getMajorVersion() > 1)) { - setTestsDisabled(true); - return; - } - - resetSQL(); - Query q = em.createQuery("SELECT f FROM AllFieldTypes f WHERE f.booleanField=true"); - q.setHint("openjpa.hint.UseLiteralInSQL", "false"); - q.getResultList(); - // The literal should be converted to a parameter marker since UseLiteralInSQL is false. - assertContainsSQL("booleanField = ?"); - - resetSQL(); - q = em.createQuery("SELECT f FROM AllFieldTypes f WHERE f.booleanField=true"); - q.setHint("openjpa.hint.UseLiteralInSQL", "true"); - q.getResultList(); - // The literal should not be converted to a parameter marker since UseLiteralInSQL is true. - // However, the literal should be converted to a 1 because we store boolean as int/smallint. - assertContainsSQL("booleanField = 1"); - - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.literals; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; + +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.jdbc.sql.DBDictionary; +import org.apache.openjpa.jdbc.sql.H2Dictionary; +import org.apache.openjpa.jdbc.sql.PostgresDictionary; +import org.apache.openjpa.persistence.simple.AllFieldTypes; +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +public class TestLiteralInSQL extends SQLListenerTestCase { + @Override + public void setUp() { + setUp(AllFieldTypes.class, "openjpa.jdbc.QuerySQLCache", "false"); + } + + public void testTrueInSQL() { + EntityManager em = emf.createEntityManager(); + + em = emf.createEntityManager(); + DBDictionary dict = ((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance(); + //Disable on Postgres for now.... + if (dict instanceof PostgresDictionary || (dict instanceof H2Dictionary && dict.getMajorVersion() > 1)) { + setTestsDisabled(true); + return; + } + + resetSQL(); + Query q = em.createQuery("SELECT f FROM AllFieldTypes f WHERE f.booleanField=true"); + q.setHint("openjpa.hint.UseLiteralInSQL", "false"); + q.getResultList(); + // The literal should be converted to a parameter marker since UseLiteralInSQL is false. + assertContainsSQL("booleanField = ?"); + + resetSQL(); + q = em.createQuery("SELECT f FROM AllFieldTypes f WHERE f.booleanField=true"); + q.setHint("openjpa.hint.UseLiteralInSQL", "true"); + q.getResultList(); + // The literal should not be converted to a parameter marker since UseLiteralInSQL is true. + // However, the literal should be converted to a 1 because we store boolean as int/smallint. + assertContainsSQL("booleanField = 1"); + + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/Author.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/Author.java index 2e801596f7..6fa8a9b5fc 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/Author.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/Author.java @@ -1,40 +1,40 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -@Entity -public class Author { - - @Id - private int id; - - @Version - private int version; - - public int getId() { return id; } - public void setId(int id) { this.id = id; } - - public int getVersion() { return version; } - public void setVersion(int version) { this.version = version; } -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +@Entity +public class Author { + + @Id + private int id; + + @Version + private int version; + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + + public int getVersion() { return version; } + public void setVersion(int version) { this.version = version; } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/Document.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/Document.java index 12fa42d241..b97f37ad29 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/Document.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/Document.java @@ -1,38 +1,38 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; - -@Entity -public class Document { - - @Id - private int id; - - @ManyToOne - private Author author; - - public int getId() { return id; } - public void setId(int id) { this.id = id; } - public Author getAuthor() { return author; } - public void setAuthor(Author author) { this.author = author; } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; + +@Entity +public class Document { + + @Id + private int id; + + @ManyToOne + private Author author; + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + public Author getAuthor() { return author; } + public void setAuthor(Author author) { this.author = author; } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/TestJoinVersionField.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/TestJoinVersionField.java index 34f8441181..34effbabbd 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/TestJoinVersionField.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/TestJoinVersionField.java @@ -1,86 +1,86 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version; - -import java.util.List; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; -import jakarta.persistence.TypedQuery; - -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -/** - * Verifies that the version field is returned when part of a join statement. - * See OPENJPA-2343. - */ -public class TestJoinVersionField extends SingleEMFTestCase { - - @Override - public void setUp(){ - setUp(CLEAR_TABLES, Author.class,Document.class); - createTestData(); - } - - public void testGetDocuments(){ - EntityManager em = emf.createEntityManager(); - String str = "SELECT doc FROM Document doc JOIN doc.author auth"; - TypedQuery query = em.createQuery(str, Document.class); - List documentList = query.getResultList(); - - for (Document doc : documentList) { - assertEquals("Author version field should have a value of 1.", - 1, doc.getAuthor().getVersion()); - } - - em.close(); - } - - /** - * Prior to OPENJPA-2343, the version field in the Author entity is returned - * as null. - */ - public void testGetDocumentsByExplicitAttributeSelection(){ - EntityManager em = emf.createEntityManager(); - String str = "SELECT doc.id, auth.id, auth.version FROM Document doc JOIN doc.author auth"; - Query query = em.createQuery(str); - List objectList = query.getResultList(); - - for (Object[] objects : objectList) { - assertEquals("Author version field should have a value of 1.",1,objects[2]); - } - } - - public void createTestData() { - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - - Author author = new Author(); - author.setId(10); - em.persist(author); - - Document document = new Document(); - document.setId(2); - document.setAuthor(author); - em.persist(document); - - em.getTransaction().commit(); - em.close(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version; + +import java.util.List; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +/** + * Verifies that the version field is returned when part of a join statement. + * See OPENJPA-2343. + */ +public class TestJoinVersionField extends SingleEMFTestCase { + + @Override + public void setUp(){ + setUp(CLEAR_TABLES, Author.class,Document.class); + createTestData(); + } + + public void testGetDocuments(){ + EntityManager em = emf.createEntityManager(); + String str = "SELECT doc FROM Document doc JOIN doc.author auth"; + TypedQuery query = em.createQuery(str, Document.class); + List documentList = query.getResultList(); + + for (Document doc : documentList) { + assertEquals("Author version field should have a value of 1.", + 1, doc.getAuthor().getVersion()); + } + + em.close(); + } + + /** + * Prior to OPENJPA-2343, the version field in the Author entity is returned + * as null. + */ + public void testGetDocumentsByExplicitAttributeSelection(){ + EntityManager em = emf.createEntityManager(); + String str = "SELECT doc.id, auth.id, auth.version FROM Document doc JOIN doc.author auth"; + Query query = em.createQuery(str); + List objectList = query.getResultList(); + + for (Object[] objects : objectList) { + assertEquals("Author version field should have a value of 1.",1,objects[2]); + } + } + + public void createTestData() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + Author author = new Author(); + author.setId(10); + em.persist(author); + + Document document = new Document(); + document.setId(2); + document.setAuthor(author); + em.persist(document); + + em.getTransaction().commit(); + em.close(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/BaseEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/BaseEntity.java index 85a8588773..2d5a9b4e26 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/BaseEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/BaseEntity.java @@ -1,31 +1,31 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version.type; - -import jakarta.persistence.MappedSuperclass; -import jakarta.persistence.Version; - -@MappedSuperclass -abstract class BaseEntity { - - @Version - protected Long version; - - public Long getVersion() { return version; } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version.type; + +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Version; + +@MappedSuperclass +abstract class BaseEntity { + + @Version + protected Long version; + + public Long getVersion() { return version; } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/ChildVersionEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/ChildVersionEntity.java index 6e7206526b..f0e96761f9 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/ChildVersionEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/ChildVersionEntity.java @@ -1,33 +1,33 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version.type; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; - -@Entity -public class ChildVersionEntity extends BaseEntity { - - @Id - private int id; - - public int getId() { return id; } - public void setId(int id) { this.id = id; } -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version.type; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class ChildVersionEntity extends BaseEntity { + + @Id + private int id; + + public int getId() { return id; } + public void setId(int id) { this.id = id; } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/LongVersionEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/LongVersionEntity.java index 407e9a9d6e..aae0a989e5 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/LongVersionEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/LongVersionEntity.java @@ -1,42 +1,41 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version.type; - -import java.io.Serializable; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -@Entity -public class LongVersionEntity implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - private int id; - - @Version - protected Long version; - - public int getId() { return id; } - public void setId(int id) { this.id = id; } - public Long getVersion() { return version; } -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version.type; + +import java.io.Serializable; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +@Entity +public class LongVersionEntity implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + private int id; + + @Version + protected Long version; + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + public Long getVersion() { return version; } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/PrimativeLongVersionEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/PrimativeLongVersionEntity.java index 0a05a733a3..50195826a8 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/PrimativeLongVersionEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/PrimativeLongVersionEntity.java @@ -1,42 +1,41 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version.type; - -import java.io.Serializable; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -@Entity -public class PrimativeLongVersionEntity implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - private int id; - - @Version - protected long version; - - public int getId() { return id; } - public void setId(int id) { this.id = id; } - public long getVersion() { return version; } -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version.type; + +import java.io.Serializable; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +@Entity +public class PrimativeLongVersionEntity implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + private int id; + + @Version + protected long version; + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + public long getVersion() { return version; } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/PrimativeShortVersionEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/PrimativeShortVersionEntity.java index 0437e18ac8..ebff0350e2 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/PrimativeShortVersionEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/PrimativeShortVersionEntity.java @@ -1,42 +1,41 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version.type; - -import java.io.Serializable; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -@Entity -public class PrimativeShortVersionEntity implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - private int id; - - @Version - protected short version; - - public int getId() { return id; } - public void setId(int id) { this.id = id; } - public short getVersion() { return version; } -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version.type; + +import java.io.Serializable; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +@Entity +public class PrimativeShortVersionEntity implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + private int id; + + @Version + protected short version; + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + public short getVersion() { return version; } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/ShortVersionEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/ShortVersionEntity.java index 356a6f8fd6..80f30f8cc1 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/ShortVersionEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/ShortVersionEntity.java @@ -1,42 +1,42 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version.type; - -import java.io.Serializable; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -@Entity -public class ShortVersionEntity implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - private int id; - - @Version - protected Short version; - - public int getId() { return id; } - public void setId(int id) { this.id = id; } - public Short getVersion() { return version; } -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version.type; + +import java.io.Serializable; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +@Entity +public class ShortVersionEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + private int id; + + @Version + protected Short version; + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + public Short getVersion() { return version; } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/TestVersionFieldType.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/TestVersionFieldType.java index 05333c794a..a9edf8e2cb 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/TestVersionFieldType.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/TestVersionFieldType.java @@ -1,101 +1,101 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version.type; - -import java.sql.Timestamp; -import java.util.List; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; - -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -/** - * Verifies that the version field is of the proper java type - * when returned from a query. See OPENJPA-2435. - */ -public class TestVersionFieldType extends SingleEMFTestCase { - - @Override - public void setUp() { - setUp(CLEAR_TABLES, LongVersionEntity.class, - ShortVersionEntity.class, PrimativeLongVersionEntity.class, - PrimativeShortVersionEntity.class, TimestampVersionEntity.class, - BaseEntity.class, ChildVersionEntity.class); - createTestData(); - } - - public void testProjectionVersionReturnType() { - verifyType(LongVersionEntity.class, Long.class); - verifyType(ShortVersionEntity.class, Short.class); - verifyType(PrimativeShortVersionEntity.class, Short.class); - verifyType(PrimativeLongVersionEntity.class, Long.class); - verifyType(ChildVersionEntity.class, Long.class); - verifyType(TimestampVersionEntity.class, Timestamp.class); - } - - public void verifyType(Class cls, Class expectedClsType) { - - EntityManager em = emf.createEntityManager(); - String str = "SELECT o.id, o.version FROM " + cls.getName() + " o"; - Query query = em.createQuery(str); - List objectList = query.getResultList(); - - for (Object[] objects : objectList) { - assertNotNull("Version should not be null.", objects[1]); - assertTrue("Type should be " + expectedClsType.getName() + - ". But it is " + objects[1].getClass(), - objects[1].getClass() == expectedClsType); - } - - em.close(); - } - - public void createTestData() { - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - - LongVersionEntity lve = new LongVersionEntity(); - lve.setId(9); - em.persist(lve); - - ShortVersionEntity sve = new ShortVersionEntity(); - sve.setId(9); - em.persist(sve); - - PrimativeShortVersionEntity psve = new PrimativeShortVersionEntity(); - psve.setId(9); - em.persist(psve); - - PrimativeLongVersionEntity plve = new PrimativeLongVersionEntity(); - plve.setId(9); - em.persist(plve); - - TimestampVersionEntity tve = new TimestampVersionEntity(); - tve.setId(9); - em.persist(tve); - - ChildVersionEntity ave = new ChildVersionEntity(); - ave.setId(9); - em.persist(ave); - - em.getTransaction().commit(); - em.close(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version.type; + +import java.sql.Timestamp; +import java.util.List; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +/** + * Verifies that the version field is of the proper java type + * when returned from a query. See OPENJPA-2435. + */ +public class TestVersionFieldType extends SingleEMFTestCase { + + @Override + public void setUp() { + setUp(CLEAR_TABLES, LongVersionEntity.class, + ShortVersionEntity.class, PrimativeLongVersionEntity.class, + PrimativeShortVersionEntity.class, TimestampVersionEntity.class, + BaseEntity.class, ChildVersionEntity.class); + createTestData(); + } + + public void testProjectionVersionReturnType() { + verifyType(LongVersionEntity.class, Long.class); + verifyType(ShortVersionEntity.class, Short.class); + verifyType(PrimativeShortVersionEntity.class, Short.class); + verifyType(PrimativeLongVersionEntity.class, Long.class); + verifyType(ChildVersionEntity.class, Long.class); + verifyType(TimestampVersionEntity.class, Timestamp.class); + } + + public void verifyType(Class cls, Class expectedClsType) { + + EntityManager em = emf.createEntityManager(); + String str = "SELECT o.id, o.version FROM " + cls.getName() + " o"; + Query query = em.createQuery(str); + List objectList = query.getResultList(); + + for (Object[] objects : objectList) { + assertNotNull("Version should not be null.", objects[1]); + assertTrue("Type should be " + expectedClsType.getName() + + ". But it is " + objects[1].getClass(), + objects[1].getClass() == expectedClsType); + } + + em.close(); + } + + public void createTestData() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + LongVersionEntity lve = new LongVersionEntity(); + lve.setId(9); + em.persist(lve); + + ShortVersionEntity sve = new ShortVersionEntity(); + sve.setId(9); + em.persist(sve); + + PrimativeShortVersionEntity psve = new PrimativeShortVersionEntity(); + psve.setId(9); + em.persist(psve); + + PrimativeLongVersionEntity plve = new PrimativeLongVersionEntity(); + plve.setId(9); + em.persist(plve); + + TimestampVersionEntity tve = new TimestampVersionEntity(); + tve.setId(9); + em.persist(tve); + + ChildVersionEntity ave = new ChildVersionEntity(); + ave.setId(9); + em.persist(ave); + + em.getTransaction().commit(); + em.close(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/TimestampVersionEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/TimestampVersionEntity.java index 0c7a591a66..a7a0084aac 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/TimestampVersionEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jpql/version/type/TimestampVersionEntity.java @@ -1,43 +1,42 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.jpql.version.type; - -import java.io.Serializable; -import java.sql.Timestamp; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -@Entity -public class TimestampVersionEntity implements Serializable { - - private static final long serialVersionUID = 1L; - - @Id - private int id; - - @Version - protected Timestamp version; - - public int getId() { return id; } - public void setId(int id) { this.id = id; } - public Timestamp getVersion() { return version; } -} - +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jpql.version.type; + +import java.io.Serializable; +import java.sql.Timestamp; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +@Entity +public class TimestampVersionEntity implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + private int id; + + @Version + protected Timestamp version; + + public int getId() { return id; } + public void setId(int id) { this.id = id; } + public Timestamp getVersion() { return version; } +} + diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/kernel/PessimisticLockEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/kernel/PessimisticLockEntity.java index 7c5a0777bb..956f35dd82 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/kernel/PessimisticLockEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/kernel/PessimisticLockEntity.java @@ -1,47 +1,47 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.kernel; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; - -@Entity -public class PessimisticLockEntity { - - @Id - int id; - - String name; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.kernel; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; + +@Entity +public class PessimisticLockEntity { + + @Id + int id; + + String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/kernel/TestPessimisticLockException.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/kernel/TestPessimisticLockException.java index eea49e5ac5..33d24227f7 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/kernel/TestPessimisticLockException.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/kernel/TestPessimisticLockException.java @@ -1,187 +1,187 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.kernel; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.LockModeType; - -import org.apache.openjpa.jdbc.conf.JDBCConfiguration; -import org.apache.openjpa.jdbc.sql.DB2Dictionary; -import org.apache.openjpa.jdbc.sql.DBDictionary; -import org.apache.openjpa.jdbc.sql.OracleDictionary; -import org.apache.openjpa.persistence.OpenJPAEntityManager; -import org.apache.openjpa.persistence.OpenJPAPersistence; -import org.apache.openjpa.persistence.PessimisticLockException; -import org.apache.openjpa.persistence.test.SQLListenerTestCase; - -public class TestPessimisticLockException extends SQLListenerTestCase { - int pKey = 1; - static boolean doSleep = true; - - @Override - public void setUp() throws Exception { - super.setUp(PessimisticLockEntity.class); - } - - /* - * This test has only been verified on DB2 and Oracle. - */ - protected boolean skipTest() { - if (emf.getConfiguration() instanceof JDBCConfiguration) { - DBDictionary inst = ((JDBCConfiguration) emf.getConfiguration()).getDBDictionaryInstance(); - return !((inst instanceof DB2Dictionary) || (inst instanceof OracleDictionary)); - } - return true; - } - - /* - * This test will verify that two threads get a an appropriate pessimistic lock - * when they both request one at the same time. See JIRA OPENJPA-2547 for a more - * detailed description of this test. - */ - public void testPessimisticLockException() { - if (!skipTest()) { - - populate(); - - TestThread t1 = new TestThread(); - TestThread t2 = new TestThread(); - t1.start(); - t2.start(); - - while ((t1.isAlive() || t2.isAlive())) { - try { - Thread.sleep(5000); - } catch (InterruptedException e) { - } - } - - // One, and only one, thread should get a PersistenceException - if (t1.gotPLEx && t2.gotPLEx) { - fail("Both threads got a PersistenceLockException! " - + "Only one thread should have received a PersistenceLockException"); - } else if (!t1.gotPLEx && !t2.gotPLEx) { - fail("Neither thread got a PersistenceLockException! " - + "One thread should have received a PersistenceLockException"); - } else if (t1.count < 2 && t2.count < 2) { - fail("PersistenceLockException was received, but not the expected number of times! " - + "One thread should have received a PersistenceLockException at least twice."); - } - } - } - - private class TestThread extends Thread { - boolean gotPLEx = false; - int count = 0; - - @Override - public synchronized void run() { - OpenJPAEntityManager oem = OpenJPAPersistence.cast(emf.createEntityManager()); - oem.getTransaction().begin(); - - PessimisticLockEntity entity = oem.find(PessimisticLockEntity.class, pKey); - - boolean locked = false; - while (!locked) { - try { - oem.getFetchPlan().setLockTimeout(5000); - oem.lock(entity, LockModeType.PESSIMISTIC_READ); - locked = true; - } catch (PessimisticLockException ple) { - gotPLEx = true; - count++; - - try { - Thread.sleep(100); - } catch (final InterruptedException ie) { - } - oem.refresh(entity); - } catch (Throwable pe) { - pe.printStackTrace(); - fail("Caught an unexepected exception: " + pe); - } - - // Only one thread needs to sleep (don't care about synchronization of 'doSleep' at this - // point - if both threads happen to get here at the same time we will test for that later.) - if (doSleep) { - doSleep = false; - try { - // Sleep log enough to ensure the other thread times out at least two times. - Thread.sleep(15000); - } catch (final InterruptedException ie) { - } - } - - if (!oem.getTransaction().getRollbackOnly()) { - oem.getTransaction().commit(); - } - } - } - } - - /* - * This test verifies the correct number of SQL statements when using a pessimistic - * lock (See JIRA OPENJPA-2449). Prior to OPENJPA-2449, when requesting a pessimistic lock - * we would do a 'select' to get the entity, and turn around and do another select to get a - * Pessimistic lock...in other words, we'd generate (on DB2) these two SQL statements for the refresh: - * - * SELECT t0.name FROM PessimisticLockEntity t0 WHERE t0.id = ? - * SELECT t0.name FROM PessimisticLockEntity t0 WHERE t0.id = ? FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS - * - * With the fix of OPENJPA-2449, we generate only one select, as follows: - * - * SELECT t0.name FROM PessimisticLockEntity t0 WHERE t0.id = ? FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS - * - * Not only does this save an SQL, but more importantly, the few millisecond delay between the two selects - * won't occur.....in a multi-threaded env this delay could cause another thread to get the lock over this - * one when the refresh occurs at the same time. - */ - public void testSQLCount() { - if (!skipTest()) { - - populate(); - resetSQL(); - - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - - PessimisticLockEntity plEnt = em.find(PessimisticLockEntity.class, pKey); - - em.refresh(plEnt, LockModeType.PESSIMISTIC_WRITE); - - plEnt.setName("test"); - em.getTransaction().commit(); - em.close(); - assertEquals("There should only be 3 SQL statements", 3, getSQLCount()); - } - } - - public void populate() { - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - - PessimisticLockEntity pt = new PessimisticLockEntity(); - pt.setId(pKey); - - em.persist(pt); - - em.getTransaction().commit(); - em.close(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.kernel; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; + +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.jdbc.sql.DB2Dictionary; +import org.apache.openjpa.jdbc.sql.DBDictionary; +import org.apache.openjpa.jdbc.sql.OracleDictionary; +import org.apache.openjpa.persistence.OpenJPAEntityManager; +import org.apache.openjpa.persistence.OpenJPAPersistence; +import org.apache.openjpa.persistence.PessimisticLockException; +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +public class TestPessimisticLockException extends SQLListenerTestCase { + int pKey = 1; + static boolean doSleep = true; + + @Override + public void setUp() throws Exception { + super.setUp(PessimisticLockEntity.class); + } + + /* + * This test has only been verified on DB2 and Oracle. + */ + protected boolean skipTest() { + if (emf.getConfiguration() instanceof JDBCConfiguration) { + DBDictionary inst = ((JDBCConfiguration) emf.getConfiguration()).getDBDictionaryInstance(); + return !((inst instanceof DB2Dictionary) || (inst instanceof OracleDictionary)); + } + return true; + } + + /* + * This test will verify that two threads get a an appropriate pessimistic lock + * when they both request one at the same time. See JIRA OPENJPA-2547 for a more + * detailed description of this test. + */ + public void testPessimisticLockException() { + if (!skipTest()) { + + populate(); + + TestThread t1 = new TestThread(); + TestThread t2 = new TestThread(); + t1.start(); + t2.start(); + + while ((t1.isAlive() || t2.isAlive())) { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + } + } + + // One, and only one, thread should get a PersistenceException + if (t1.gotPLEx && t2.gotPLEx) { + fail("Both threads got a PersistenceLockException! " + + "Only one thread should have received a PersistenceLockException"); + } else if (!t1.gotPLEx && !t2.gotPLEx) { + fail("Neither thread got a PersistenceLockException! " + + "One thread should have received a PersistenceLockException"); + } else if (t1.count < 2 && t2.count < 2) { + fail("PersistenceLockException was received, but not the expected number of times! " + + "One thread should have received a PersistenceLockException at least twice."); + } + } + } + + private class TestThread extends Thread { + boolean gotPLEx = false; + int count = 0; + + @Override + public synchronized void run() { + OpenJPAEntityManager oem = OpenJPAPersistence.cast(emf.createEntityManager()); + oem.getTransaction().begin(); + + PessimisticLockEntity entity = oem.find(PessimisticLockEntity.class, pKey); + + boolean locked = false; + while (!locked) { + try { + oem.getFetchPlan().setLockTimeout(5000); + oem.lock(entity, LockModeType.PESSIMISTIC_READ); + locked = true; + } catch (PessimisticLockException ple) { + gotPLEx = true; + count++; + + try { + Thread.sleep(100); + } catch (final InterruptedException ie) { + } + oem.refresh(entity); + } catch (Throwable pe) { + pe.printStackTrace(); + fail("Caught an unexepected exception: " + pe); + } + + // Only one thread needs to sleep (don't care about synchronization of 'doSleep' at this + // point - if both threads happen to get here at the same time we will test for that later.) + if (doSleep) { + doSleep = false; + try { + // Sleep log enough to ensure the other thread times out at least two times. + Thread.sleep(15000); + } catch (final InterruptedException ie) { + } + } + + if (!oem.getTransaction().getRollbackOnly()) { + oem.getTransaction().commit(); + } + } + } + } + + /* + * This test verifies the correct number of SQL statements when using a pessimistic + * lock (See JIRA OPENJPA-2449). Prior to OPENJPA-2449, when requesting a pessimistic lock + * we would do a 'select' to get the entity, and turn around and do another select to get a + * Pessimistic lock...in other words, we'd generate (on DB2) these two SQL statements for the refresh: + * + * SELECT t0.name FROM PessimisticLockEntity t0 WHERE t0.id = ? + * SELECT t0.name FROM PessimisticLockEntity t0 WHERE t0.id = ? FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS + * + * With the fix of OPENJPA-2449, we generate only one select, as follows: + * + * SELECT t0.name FROM PessimisticLockEntity t0 WHERE t0.id = ? FOR READ ONLY WITH RR USE AND KEEP UPDATE LOCKS + * + * Not only does this save an SQL, but more importantly, the few millisecond delay between the two selects + * won't occur.....in a multi-threaded env this delay could cause another thread to get the lock over this + * one when the refresh occurs at the same time. + */ + public void testSQLCount() { + if (!skipTest()) { + + populate(); + resetSQL(); + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + PessimisticLockEntity plEnt = em.find(PessimisticLockEntity.class, pKey); + + em.refresh(plEnt, LockModeType.PESSIMISTIC_WRITE); + + plEnt.setName("test"); + em.getTransaction().commit(); + em.close(); + assertEquals("There should only be 3 SQL statements", 3, getSQLCount()); + } + } + + public void populate() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + PessimisticLockEntity pt = new PessimisticLockEntity(); + pt.setId(pKey); + + em.persist(pt); + + em.getTransaction().commit(); + em.close(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItem.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItem.java index b4d65e3f45..82aa7677dd 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItem.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItem.java @@ -1,142 +1,142 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.merge; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.IdClass; -import jakarta.persistence.Table; - -@Entity -@Table( name = "ITEM_TABLE" ) -// Remove this @IdClass, and one of the @Id (i.e. use a single PK, not a compound PK -// and the test will work fine!!!!! -@IdClass( LineItemPK.class ) -public class LineItem { - @Id - @Column( name = "ORDER_ID", nullable = false ) - private Long orderId; - - @Id - @Column( name = "ITEM_ID", nullable = false ) - private Long itemId; - - @Column( name = "PRODUCT_NAME", nullable = false ) - private String productName; - - @Column( name = "QUANTITY", nullable = false ) - private int quantity; - - @Column( name = "PRICE", nullable = false ) - private float price; - - public LineItem() { - } - - public LineItem( String productName, int quantity, float price ) { - this(); - this.productName = productName; - this.quantity = quantity; - this.price = price; - } - - public String getProductName() { - return productName; - } - - public void setProductName(String productName) { - this.productName = productName; - } - - public int getQuantity() { - return quantity; - } - - public void setQuantity(int quantity) { - this.quantity = quantity; - } - - public float getPrice() { - return price; - } - - public void setPrice(float price) { - this.price = price; - } - - public Long getOrderId() { - return orderId; - } - - public void setOrderId(Long orderId) { - this.orderId = orderId; - } - - public Long getItemId() { - return itemId; - } - - public void setItemId(Long itemId) { - this.itemId = itemId; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((itemId == null) ? 0 : itemId.hashCode()); - result = prime * result + ((orderId == null) ? 0 : orderId.hashCode()); - result = prime * result + Float.floatToIntBits(price); - result = prime * result + ((productName == null) ? 0 : productName.hashCode()); - result = prime * result + quantity; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - LineItem other = (LineItem) obj; - if (itemId == null) { - if (other.itemId != null) - return false; - } else if (!itemId.equals(other.itemId)) - return false; - if (orderId == null) { - if (other.orderId != null) - return false; - } else if (!orderId.equals(other.orderId)) - return false; - if (Float.floatToIntBits(price) != Float.floatToIntBits(other.price)) - return false; - if (productName == null) { - if (other.productName != null) - return false; - } else if (!productName.equals(other.productName)) - return false; - if (quantity != other.quantity) - return false; - return true; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.merge; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; + +@Entity +@Table( name = "ITEM_TABLE" ) +// Remove this @IdClass, and one of the @Id (i.e. use a single PK, not a compound PK +// and the test will work fine!!!!! +@IdClass( LineItemPK.class ) +public class LineItem { + @Id + @Column( name = "ORDER_ID", nullable = false ) + private Long orderId; + + @Id + @Column( name = "ITEM_ID", nullable = false ) + private Long itemId; + + @Column( name = "PRODUCT_NAME", nullable = false ) + private String productName; + + @Column( name = "QUANTITY", nullable = false ) + private int quantity; + + @Column( name = "PRICE", nullable = false ) + private float price; + + public LineItem() { + } + + public LineItem( String productName, int quantity, float price ) { + this(); + this.productName = productName; + this.quantity = quantity; + this.price = price; + } + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public float getPrice() { + return price; + } + + public void setPrice(float price) { + this.price = price; + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getItemId() { + return itemId; + } + + public void setItemId(Long itemId) { + this.itemId = itemId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((itemId == null) ? 0 : itemId.hashCode()); + result = prime * result + ((orderId == null) ? 0 : orderId.hashCode()); + result = prime * result + Float.floatToIntBits(price); + result = prime * result + ((productName == null) ? 0 : productName.hashCode()); + result = prime * result + quantity; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LineItem other = (LineItem) obj; + if (itemId == null) { + if (other.itemId != null) + return false; + } else if (!itemId.equals(other.itemId)) + return false; + if (orderId == null) { + if (other.orderId != null) + return false; + } else if (!orderId.equals(other.orderId)) + return false; + if (Float.floatToIntBits(price) != Float.floatToIntBits(other.price)) + return false; + if (productName == null) { + if (other.productName != null) + return false; + } else if (!productName.equals(other.productName)) + return false; + if (quantity != other.quantity) + return false; + return true; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItemPK.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItemPK.java index 746e0ac519..6c86353442 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItemPK.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/LineItemPK.java @@ -1,85 +1,85 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.merge; - -import java.io.Serializable; - -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; - -@Embeddable -public class LineItemPK implements Serializable { - private static final long serialVersionUID = -8657894635702714413L; - - @Column( name = "ORDER_ID", nullable = false ) - private Long orderId; - - @Column( name = "ITEM_ID", nullable = false ) - private Long itemId; - - public LineItemPK() { - } - - public Long getOrderId() { - return orderId; - } - - public void setOrderId(Long orderId) { - this.orderId = orderId; - } - - public Long getItemId() { - return itemId; - } - - public void setItemId(Long itemId) { - this.itemId = itemId; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((itemId == null) ? 0 : itemId.hashCode()); - result = prime * result + ((orderId == null) ? 0 : orderId.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - LineItemPK other = (LineItemPK) obj; - if (itemId == null) { - if (other.itemId != null) - return false; - } else if (!itemId.equals(other.itemId)) - return false; - if (orderId == null) { - if (other.orderId != null) - return false; - } else if (!orderId.equals(other.orderId)) - return false; - return true; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.merge; + +import java.io.Serializable; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; + +@Embeddable +public class LineItemPK implements Serializable { + private static final long serialVersionUID = -8657894635702714413L; + + @Column( name = "ORDER_ID", nullable = false ) + private Long orderId; + + @Column( name = "ITEM_ID", nullable = false ) + private Long itemId; + + public LineItemPK() { + } + + public Long getOrderId() { + return orderId; + } + + public void setOrderId(Long orderId) { + this.orderId = orderId; + } + + public Long getItemId() { + return itemId; + } + + public void setItemId(Long itemId) { + this.itemId = itemId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((itemId == null) ? 0 : itemId.hashCode()); + result = prime * result + ((orderId == null) ? 0 : orderId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LineItemPK other = (LineItemPK) obj; + if (itemId == null) { + if (other.itemId != null) + return false; + } else if (!itemId.equals(other.itemId)) + return false; + if (orderId == null) { + if (other.orderId != null) + return false; + } else if (!orderId.equals(other.orderId)) + return false; + return true; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/Order.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/Order.java index 58f393b4bb..eb91d5705c 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/Order.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/Order.java @@ -1,91 +1,91 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.merge; - -import java.sql.Date; -import java.util.ArrayList; -import java.util.List; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import jakarta.persistence.Temporal; -import jakarta.persistence.TemporalType; - -@Entity -@Table( name = "ORDER_TABLE" ) -public class Order { - @Id - @Column( name = "ID", nullable = false ) - private Long id; - - @Column( name = "ENTRY_DATE", nullable = false ) - @Temporal(TemporalType.TIMESTAMP) - private Date orderEntry; - - // When using a List, things fails...using a Set all works fine. - @OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL ) - @JoinColumn( name = "ORDER_ID", referencedColumnName = "ID" ) - private List items; - - public Order() { - orderEntry = new Date( System.currentTimeMillis() ); - items = new ArrayList<>(); - } - - public Order( long id ) { - this(); - this.id = id; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public Date getOrderEntry() { - return orderEntry; - } - - public void setOrderEntry(Date orderEntry) { - this.orderEntry = orderEntry; - } - - public void addItem( LineItem item ) { - items.add(item); - item.setOrderId(id); - item.setItemId((long)items.size() ); - } - - public List getItems() { - return items; - } - - public void setItems(List items) { - this.items = items; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.merge; + +import java.sql.Date; +import java.util.ArrayList; +import java.util.List; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; + +@Entity +@Table( name = "ORDER_TABLE" ) +public class Order { + @Id + @Column( name = "ID", nullable = false ) + private Long id; + + @Column( name = "ENTRY_DATE", nullable = false ) + @Temporal(TemporalType.TIMESTAMP) + private Date orderEntry; + + // When using a List, things fails...using a Set all works fine. + @OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL ) + @JoinColumn( name = "ORDER_ID", referencedColumnName = "ID" ) + private List items; + + public Order() { + orderEntry = new Date( System.currentTimeMillis() ); + items = new ArrayList<>(); + } + + public Order( long id ) { + this(); + this.id = id; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Date getOrderEntry() { + return orderEntry; + } + + public void setOrderEntry(Date orderEntry) { + this.orderEntry = orderEntry; + } + + public void addItem( LineItem item ) { + items.add(item); + item.setOrderId(id); + item.setItemId((long)items.size() ); + } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/TestMultipleMerge.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/TestMultipleMerge.java index 21bbffb590..d7e65f07bc 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/TestMultipleMerge.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/merge/TestMultipleMerge.java @@ -1,88 +1,88 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.merge; - -import java.sql.Date; - -import jakarta.persistence.EntityManager; - -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -/* - * See OPENJPA-2603 for details. - */ -public class TestMultipleMerge extends SingleEMFTestCase { - - @Override - public void setUp() { - //Since a JPA 1.0 p.xml file is used the property "NonDefaultMappingAllowed=true" is - //needed for this test. This is needed since Order uses an @JoinColumn; something - //not allowed in 1.0 (or at least a grey area in the spec) on an @OneToMany. - setUp("openjpa.Compatibility", "NonDefaultMappingAllowed=true", - CLEAR_TABLES, Order.class, LineItemPK.class, LineItem.class); - } - - public void testMultiMerge() { - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - Order order = new Order(1L); - - LineItem item = new LineItem( "my product", 44, 4.99f ); - order.addItem(item); - - //NOTE: Notice that throughout the rest of the test the unmanaged order is merged. - //Throughout the rest of the test we should do a 'order = em.merge(order)', or - //something to that effect (i.e. use the 'managed' order). However, technically - //speaking merging the unmanaged order is not wrong, albeit odd and potentially - //error prone. - em.merge(order); - em.getTransaction().commit(); - - em.getTransaction().begin(); - LineItem additional = new LineItem( "My second product", 1, 999.95f ); - order.addItem(additional); - order.setOrderEntry( new Date( System.currentTimeMillis() ) ); - em.merge(order); - //NOTE: do a flush here and all works fine: - //em.flush(); - em.merge(order); - //Prior to fix, an exception occurred on the commit. - em.getTransaction().commit(); - - em.clear(); - - //OK, good, we no longer get an exception, to be double certain - //all is well, lets verify that the expected LineItems are in the DB. - LineItemPK liPK = new LineItemPK(); - liPK.setItemId(1L); - liPK.setOrderId(1L); - LineItem li = em.find(LineItem.class, liPK); - - assertNotNull(li); - assertEquals(item.getProductName(), li.getProductName()); - - liPK.setItemId(2L); - liPK.setOrderId(1L); - li = em.find(LineItem.class, liPK); - assertNotNull(li); - assertEquals(additional.getProductName(), li.getProductName()); - - em.close(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.merge; + +import java.sql.Date; + +import jakarta.persistence.EntityManager; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +/* + * See OPENJPA-2603 for details. + */ +public class TestMultipleMerge extends SingleEMFTestCase { + + @Override + public void setUp() { + //Since a JPA 1.0 p.xml file is used the property "NonDefaultMappingAllowed=true" is + //needed for this test. This is needed since Order uses an @JoinColumn; something + //not allowed in 1.0 (or at least a grey area in the spec) on an @OneToMany. + setUp("openjpa.Compatibility", "NonDefaultMappingAllowed=true", + CLEAR_TABLES, Order.class, LineItemPK.class, LineItem.class); + } + + public void testMultiMerge() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + Order order = new Order(1L); + + LineItem item = new LineItem( "my product", 44, 4.99f ); + order.addItem(item); + + //NOTE: Notice that throughout the rest of the test the unmanaged order is merged. + //Throughout the rest of the test we should do a 'order = em.merge(order)', or + //something to that effect (i.e. use the 'managed' order). However, technically + //speaking merging the unmanaged order is not wrong, albeit odd and potentially + //error prone. + em.merge(order); + em.getTransaction().commit(); + + em.getTransaction().begin(); + LineItem additional = new LineItem( "My second product", 1, 999.95f ); + order.addItem(additional); + order.setOrderEntry( new Date( System.currentTimeMillis() ) ); + em.merge(order); + //NOTE: do a flush here and all works fine: + //em.flush(); + em.merge(order); + //Prior to fix, an exception occurred on the commit. + em.getTransaction().commit(); + + em.clear(); + + //OK, good, we no longer get an exception, to be double certain + //all is well, lets verify that the expected LineItems are in the DB. + LineItemPK liPK = new LineItemPK(); + liPK.setItemId(1L); + liPK.setOrderId(1L); + LineItem li = em.find(LineItem.class, liPK); + + assertNotNull(li); + assertEquals(item.getProductName(), li.getProductName()); + + liPK.setItemId(2L); + liPK.setOrderId(1L); + li = em.find(LineItem.class, liPK); + assertNotNull(li); + assertEquals(additional.getProductName(), li.getProductName()); + + em.close(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/optlockex/timestamp/TestTimestampOptLockEx.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/optlockex/timestamp/TestTimestampOptLockEx.java index 32a95a2dd1..bcbc6b9e3b 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/optlockex/timestamp/TestTimestampOptLockEx.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/optlockex/timestamp/TestTimestampOptLockEx.java @@ -1,129 +1,129 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.optlockex.timestamp; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityTransaction; - -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -/* - * Test create for JIRA OPENJPA-2476, see it for a very detailed - * description of the issue. - */ -public class TestTimestampOptLockEx extends SingleEMFTestCase { - - @Override - public void setUp() { - // By default we'd round a Timestamp to the nearest millisecond on Oracle (see DBDictionary.datePrecision - // and DBDictionary.setTimestamp) and nearest microsecond on DB2 (see DB2Dictionary.datePrecision and - // DBDictionary.setTimestamp) when sending the value to the db...if we change datePrecision to 1, we round to - // the nearest nanosecond. On DB2 and Oracle, it appears the default precision is microseconds but it seems - // DB2 truncates (no rounding) to microsecond for anything it is given with greater precision, whereas Oracle - // rounds. So in the case of DB2, this test will pass if datePrecision=1, but still fails on Oracle. - // On the other hand, if we set the datePrecision to 1000000 and run against DB2, the test will fail. - - // This test requires datePrecision to be set to the same precision as the Timestamp column. - // I've only been testing on Oracle and DB2 and not sure how other DBs treat a Timestamps precision - // by default. In VersionTSEntity I use a Timestamp(3) but this is not supported on, at least, Derby - // and older versions of DB2...at this time I'll enable only on Oracle. - setSupportedDatabases(org.apache.openjpa.jdbc.sql.OracleDictionary.class); - if (isTestsDisabled()) { - return; - } - - // Set datePrecision=1000000 for Oracle since we are using Timestamp(3)....on Oracle - // the default is 1000000 so we shouldn't need to set it, but lets set it to future - // proof the test. - super.setUp(DROP_TABLES, "openjpa.jdbc.DBDictionary", "datePrecision=1000000", VersionTSEntity.class); - } - - public void testUpdate() { - poplulate(); - //This loop is necessary since we need a timestamp which has been rounded up - //by the database, or by OpenJPA such that the in-memory version of the Timestamp - //varies from that which is in the database. - for (int i = 0; i < 5000; i++) { - EntityManager em = emf.createEntityManager(); - EntityTransaction tx = em.getTransaction(); - - // Find an existing VersionTSEntity: - // stored with microsecond precision, e.g. 2014-01-21 13:16:46.595428 - VersionTSEntity t = em.find(VersionTSEntity.class, 1); - - tx.begin(); - t.setSomeInt(t.getSomeInt() + 1); - t = em.merge(t); - - tx.commit(); - // If this clear is removed the test works fine. - em.clear(); - - // Lets say at this point the 'in-memory' timestamp is: 2014-01-22 07:22:11.548778567. What we - // actually sent to the DB (via the previous merge) is by default rounded (see DBDictionary.setTimestamp) - // to the nearest millisecond on Oracle (see DBDictionary.datePrecision) and nearest microsecond on - // DB2 (see DB2Dictionary.datePrecision) when sending the value to the db. - // Therefore, what we actually send to the db is: 2014-01-22 07:22:11.548779 (for DB2) or - // 2014-01-22 07:22:11.549 (for Oracle). Notice in either case we rounded up. - - // now, do a merge with the unchanged entity - tx = em.getTransaction(); - tx.begin(); - - t = em.merge(t); // this results in a select of VersionTSEntity - - //This 'fixes' the issue (but customer doesn't really want to add this): - //em.refresh(t); - - // Here is where things get interesting.....an error will happen here when the timestamp - // has been rounded up, as I'll explain: - // As part of this merge/commit, we select the timestamp from the db to get its value - // (see method ColumnVersionStrategy.checkVersion below), i.e: - // 'SELECT t0.updateTimestamp FROM VersionTSEntity t0 WHERE t0.id = ?'. - // We then compare the 'in-memory' timestamp to that which we got back from the DB, i.e. on - // DB2 we compare: - // in-mem: 2014-01-22 07:22:11.548778567 - // from db: 2014-01-22 07:22:11.548779 - // Because these do not 'compare' properly (the db version is greater), we throw the OptimisticLockEx!! - // For completeness, lets look at an example where the timestamp is as follows after the above - // update: 2014-01-22 07:22:11.548771234. We would send to DB2 - // the following value: 2014-01-22 07:22:11.548771. Then, as part of the very last merge/commit, we'd - // compare: - // in-mem: 2014-01-22 07:22:11.548771234 - // from db: 2014-01-22 07:22:11.548771 - // These two would 'compare' properly (the db version is lesser), as such we would not throw an - // OptLockEx and the test works fine. - tx.commit(); - em.close(); - } - } - - public void poplulate(){ - EntityManager em = emf.createEntityManager(); - EntityTransaction tx = em.getTransaction(); - tx.begin(); - VersionTSEntity r = new VersionTSEntity(); - - r.setId(1L); - r.setSomeInt(0); - em.persist(r); - tx.commit(); - em.close(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.optlockex.timestamp; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityTransaction; + +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +/* + * Test create for JIRA OPENJPA-2476, see it for a very detailed + * description of the issue. + */ +public class TestTimestampOptLockEx extends SingleEMFTestCase { + + @Override + public void setUp() { + // By default we'd round a Timestamp to the nearest millisecond on Oracle (see DBDictionary.datePrecision + // and DBDictionary.setTimestamp) and nearest microsecond on DB2 (see DB2Dictionary.datePrecision and + // DBDictionary.setTimestamp) when sending the value to the db...if we change datePrecision to 1, we round to + // the nearest nanosecond. On DB2 and Oracle, it appears the default precision is microseconds but it seems + // DB2 truncates (no rounding) to microsecond for anything it is given with greater precision, whereas Oracle + // rounds. So in the case of DB2, this test will pass if datePrecision=1, but still fails on Oracle. + // On the other hand, if we set the datePrecision to 1000000 and run against DB2, the test will fail. + + // This test requires datePrecision to be set to the same precision as the Timestamp column. + // I've only been testing on Oracle and DB2 and not sure how other DBs treat a Timestamps precision + // by default. In VersionTSEntity I use a Timestamp(3) but this is not supported on, at least, Derby + // and older versions of DB2...at this time I'll enable only on Oracle. + setSupportedDatabases(org.apache.openjpa.jdbc.sql.OracleDictionary.class); + if (isTestsDisabled()) { + return; + } + + // Set datePrecision=1000000 for Oracle since we are using Timestamp(3)....on Oracle + // the default is 1000000 so we shouldn't need to set it, but lets set it to future + // proof the test. + super.setUp(DROP_TABLES, "openjpa.jdbc.DBDictionary", "datePrecision=1000000", VersionTSEntity.class); + } + + public void testUpdate() { + poplulate(); + //This loop is necessary since we need a timestamp which has been rounded up + //by the database, or by OpenJPA such that the in-memory version of the Timestamp + //varies from that which is in the database. + for (int i = 0; i < 5000; i++) { + EntityManager em = emf.createEntityManager(); + EntityTransaction tx = em.getTransaction(); + + // Find an existing VersionTSEntity: + // stored with microsecond precision, e.g. 2014-01-21 13:16:46.595428 + VersionTSEntity t = em.find(VersionTSEntity.class, 1); + + tx.begin(); + t.setSomeInt(t.getSomeInt() + 1); + t = em.merge(t); + + tx.commit(); + // If this clear is removed the test works fine. + em.clear(); + + // Lets say at this point the 'in-memory' timestamp is: 2014-01-22 07:22:11.548778567. What we + // actually sent to the DB (via the previous merge) is by default rounded (see DBDictionary.setTimestamp) + // to the nearest millisecond on Oracle (see DBDictionary.datePrecision) and nearest microsecond on + // DB2 (see DB2Dictionary.datePrecision) when sending the value to the db. + // Therefore, what we actually send to the db is: 2014-01-22 07:22:11.548779 (for DB2) or + // 2014-01-22 07:22:11.549 (for Oracle). Notice in either case we rounded up. + + // now, do a merge with the unchanged entity + tx = em.getTransaction(); + tx.begin(); + + t = em.merge(t); // this results in a select of VersionTSEntity + + //This 'fixes' the issue (but customer doesn't really want to add this): + //em.refresh(t); + + // Here is where things get interesting.....an error will happen here when the timestamp + // has been rounded up, as I'll explain: + // As part of this merge/commit, we select the timestamp from the db to get its value + // (see method ColumnVersionStrategy.checkVersion below), i.e: + // 'SELECT t0.updateTimestamp FROM VersionTSEntity t0 WHERE t0.id = ?'. + // We then compare the 'in-memory' timestamp to that which we got back from the DB, i.e. on + // DB2 we compare: + // in-mem: 2014-01-22 07:22:11.548778567 + // from db: 2014-01-22 07:22:11.548779 + // Because these do not 'compare' properly (the db version is greater), we throw the OptimisticLockEx!! + // For completeness, lets look at an example where the timestamp is as follows after the above + // update: 2014-01-22 07:22:11.548771234. We would send to DB2 + // the following value: 2014-01-22 07:22:11.548771. Then, as part of the very last merge/commit, we'd + // compare: + // in-mem: 2014-01-22 07:22:11.548771234 + // from db: 2014-01-22 07:22:11.548771 + // These two would 'compare' properly (the db version is lesser), as such we would not throw an + // OptLockEx and the test works fine. + tx.commit(); + em.close(); + } + } + + public void poplulate(){ + EntityManager em = emf.createEntityManager(); + EntityTransaction tx = em.getTransaction(); + tx.begin(); + VersionTSEntity r = new VersionTSEntity(); + + r.setId(1L); + r.setSomeInt(0); + em.persist(r); + tx.commit(); + em.close(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/optlockex/timestamp/VersionTSEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/optlockex/timestamp/VersionTSEntity.java index 76f0c22ae1..2576a2d452 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/optlockex/timestamp/VersionTSEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/optlockex/timestamp/VersionTSEntity.java @@ -1,63 +1,63 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.optlockex.timestamp; - -import java.io.Serializable; -import java.sql.Timestamp; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Version; - -@Entity -public class VersionTSEntity implements Serializable { - - private static final long serialVersionUID = 2948711625184868242L; - - @Id - private Long id; - - @Version - @Column(columnDefinition="TIMESTAMP(3)") - private Timestamp updateTimestamp; - - private Integer someInt; - - public Long getId() { - return this.id; - } - - public void setId(Long id) { - this.id = id; - } - - public Timestamp getUpdateTimestamp() { - return this.updateTimestamp; - } - - public void setSomeInt(Integer someInt) { - this.someInt = someInt; - - } - - public Integer getSomeInt() { - return someInt; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.optlockex.timestamp; + +import java.io.Serializable; +import java.sql.Timestamp; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Version; + +@Entity +public class VersionTSEntity implements Serializable { + + private static final long serialVersionUID = 2948711625184868242L; + + @Id + private Long id; + + @Version + @Column(columnDefinition="TIMESTAMP(3)") + private Timestamp updateTimestamp; + + private Integer someInt; + + public Long getId() { + return this.id; + } + + public void setId(Long id) { + this.id = id; + } + + public Timestamp getUpdateTimestamp() { + return this.updateTimestamp; + } + + public void setSomeInt(Integer someInt) { + this.someInt = someInt; + + } + + public Integer getSomeInt() { + return someInt; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUDefaultSchemaEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUDefaultSchemaEntity.java index 11096ea372..9ebf89911d 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUDefaultSchemaEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUDefaultSchemaEntity.java @@ -1,47 +1,47 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.pudefaults; - -import java.io.Serializable; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.SequenceGenerator; - -/* - * An entity which has a sequence where the sequence doesn't defined a schema, - * as such the persistence-unit-default schema (see pudefaults-orm.xml file) - * should be used when SQL operations are performed on the sequence. - */ -@Entity -public class PUDefaultSchemaEntity implements Serializable { - - private static final long serialVersionUID = 2134948659397762341L; - - @Id - @SequenceGenerator(name = "Seq_4DefaultSchema", sequenceName = "SeqName_4DefaultSchema") - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4DefaultSchema") - @Column(name = "ID") - private long id; - - public long getId() { return id; } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.pudefaults; + +import java.io.Serializable; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; + +/* + * An entity which has a sequence where the sequence doesn't defined a schema, + * as such the persistence-unit-default schema (see pudefaults-orm.xml file) + * should be used when SQL operations are performed on the sequence. + */ +@Entity +public class PUDefaultSchemaEntity implements Serializable { + + private static final long serialVersionUID = 2134948659397762341L; + + @Id + @SequenceGenerator(name = "Seq_4DefaultSchema", sequenceName = "SeqName_4DefaultSchema") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4DefaultSchema") + @Column(name = "ID") + private long id; + + public long getId() { return id; } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInSequenceAnnotationEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInSequenceAnnotationEntity.java index 2eccd1eb58..b73312df6b 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInSequenceAnnotationEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInSequenceAnnotationEntity.java @@ -1,48 +1,48 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.pudefaults; - -import java.io.Serializable; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.SequenceGenerator; - -/* - * An entity which has a sequence where the sequence defines a schema, - * as such the schema in the sequence annotation should take precedence over - * the persistence-unit-default schema (see pudefaults-orm.xml file). - */ -@Entity -public class PUSchemaInSequenceAnnotationEntity implements Serializable { - - private static final long serialVersionUID = 2472845479260320080L; - - @Id - @SequenceGenerator(name = "Seq_4AnnoSequenceSchema", sequenceName = "SeqName_4AnnoSequenceSchema", - schema = "schemaInSequenceAnnotation") - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4AnnoSequenceSchema") - @Column(name = "ID") - private long id; - - public long getId() { return id; } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.pudefaults; + +import java.io.Serializable; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; + +/* + * An entity which has a sequence where the sequence defines a schema, + * as such the schema in the sequence annotation should take precedence over + * the persistence-unit-default schema (see pudefaults-orm.xml file). + */ +@Entity +public class PUSchemaInSequenceAnnotationEntity implements Serializable { + + private static final long serialVersionUID = 2472845479260320080L; + + @Id + @SequenceGenerator(name = "Seq_4AnnoSequenceSchema", sequenceName = "SeqName_4AnnoSequenceSchema", + schema = "schemaInSequenceAnnotation") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4AnnoSequenceSchema") + @Column(name = "ID") + private long id; + + public long getId() { return id; } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInSequenceMappingEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInSequenceMappingEntity.java index 3857a5aa79..46d6b18eab 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInSequenceMappingEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInSequenceMappingEntity.java @@ -1,48 +1,48 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.pudefaults; - -import java.io.Serializable; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.SequenceGenerator; - -/* - * An entity which has a sequence where the sequence defines a schema, - * as such the schema in the sequence annotation should take precedence over - * the persistence-unit-default schema (see pudefaults-orm.xml file). - */ -@Entity -public class PUSchemaInSequenceMappingEntity implements Serializable { - - private static final long serialVersionUID = 2472845479260320080L; - - @Id - @SequenceGenerator(name = "Seq_4SequenceMappingSchema", sequenceName = "SeqName_4SequenceMappingSchema", - schema = "schemaInSequenceAnnotation") - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4SequenceMappingSchema") - @Column(name = "ID") - private long id; - - public long getId() { return id; } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.pudefaults; + +import java.io.Serializable; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; + +/* + * An entity which has a sequence where the sequence defines a schema, + * as such the schema in the sequence annotation should take precedence over + * the persistence-unit-default schema (see pudefaults-orm.xml file). + */ +@Entity +public class PUSchemaInSequenceMappingEntity implements Serializable { + + private static final long serialVersionUID = 2472845479260320080L; + + @Id + @SequenceGenerator(name = "Seq_4SequenceMappingSchema", sequenceName = "SeqName_4SequenceMappingSchema", + schema = "schemaInSequenceAnnotation") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4SequenceMappingSchema") + @Column(name = "ID") + private long id; + + public long getId() { return id; } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInTableAnnotationEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInTableAnnotationEntity.java index e084bc2124..881d101133 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInTableAnnotationEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInTableAnnotationEntity.java @@ -1,49 +1,49 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.pudefaults; - -import java.io.Serializable; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.SequenceGenerator; -import jakarta.persistence.Table; - -/* - * An entity which has a @Table annotation which contains a schema name, - * as such schema in the annotation should take precedence over the - * persistence-unit-default schema (see pudefaults-orm.xml file). - */ -@Entity -@Table(name = "PUSchemaInTable", schema = "schemaInTableAnnotation") -public class PUSchemaInTableAnnotationEntity implements Serializable { - - private static final long serialVersionUID = -566154189043208199L; - - @Id - @SequenceGenerator(name = "Seq_4AnnoTableSchema", sequenceName = "SeqName_4AnnoTableSchema") - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4AnnoTableSchema") - @Column(name = "ID") - private long id; - - public long getId() { return id; } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.pudefaults; + +import java.io.Serializable; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + +/* + * An entity which has a @Table annotation which contains a schema name, + * as such schema in the annotation should take precedence over the + * persistence-unit-default schema (see pudefaults-orm.xml file). + */ +@Entity +@Table(name = "PUSchemaInTable", schema = "schemaInTableAnnotation") +public class PUSchemaInTableAnnotationEntity implements Serializable { + + private static final long serialVersionUID = -566154189043208199L; + + @Id + @SequenceGenerator(name = "Seq_4AnnoTableSchema", sequenceName = "SeqName_4AnnoTableSchema") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4AnnoTableSchema") + @Column(name = "ID") + private long id; + + public long getId() { return id; } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInTableMappingEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInTableMappingEntity.java index 2e1db97d5b..0ccc3cc6ff 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInTableMappingEntity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/PUSchemaInTableMappingEntity.java @@ -1,51 +1,51 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.pudefaults; - -import java.io.Serializable; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.SequenceGenerator; -import jakarta.persistence.Table; - -/* - * An entity which has a @Table annotation which contains a schema name, - * as such schema in the annotation should take precedence over the - * persistence-unit-default schema (see pudefaults-orm.xml file). However, - * the schema has been overridden in the mapping file as such the - * schema in the mapping file trumps all. - */ -@Entity -@Table(name = "PUSchemaInTableMapping", schema = "schemaInTableAnnotation") -public class PUSchemaInTableMappingEntity implements Serializable { - - private static final long serialVersionUID = -566154189043208199L; - - @Id - @SequenceGenerator(name = "Seq_4TableMappingSchema", sequenceName = "SeqName_4TableMappingSchema") - @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4TableMappingSchema") - @Column(name = "ID") - private long id; - - public long getId() { return id; } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.pudefaults; + +import java.io.Serializable; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + +/* + * An entity which has a @Table annotation which contains a schema name, + * as such schema in the annotation should take precedence over the + * persistence-unit-default schema (see pudefaults-orm.xml file). However, + * the schema has been overridden in the mapping file as such the + * schema in the mapping file trumps all. + */ +@Entity +@Table(name = "PUSchemaInTableMapping", schema = "schemaInTableAnnotation") +public class PUSchemaInTableMappingEntity implements Serializable { + + private static final long serialVersionUID = -566154189043208199L; + + @Id + @SequenceGenerator(name = "Seq_4TableMappingSchema", sequenceName = "SeqName_4TableMappingSchema") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Seq_4TableMappingSchema") + @Column(name = "ID") + private long id; + + public long getId() { return id; } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/TestOpenJPASchemaPUDefault.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/TestOpenJPASchemaPUDefault.java index bee82ecfa4..9ccafb0106 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/TestOpenJPASchemaPUDefault.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/TestOpenJPASchemaPUDefault.java @@ -1,67 +1,67 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.pudefaults; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityTransaction; - -import org.apache.openjpa.persistence.test.SQLListenerTestCase; - -/* - * OPENJPA-2704: These tests expand on TestSchemaPUDefault to verify - * that a schema defined in an orm's persistence-unit-default is - * overriden by the "openjpa.jdbc.Schema" property . - */ -public class TestOpenJPASchemaPUDefault extends SQLListenerTestCase { - - @Override - public void setUp() throws Exception { - super.setUp(PUDefaultSchemaEntity.class, PUSchemaInSequenceAnnotationEntity.class, - PUSchemaInTableAnnotationEntity.class, PUSchemaInTableMappingEntity.class, - PUSchemaInSequenceMappingEntity.class); - setSupportedDatabases(org.apache.openjpa.jdbc.sql.DB2Dictionary.class); - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - } - - @Override - protected String getPersistenceUnitName() { - return "overrideMappingSchema"; - } - - public void testOpenJPASchemaOverridesORM() { - persist(new PUDefaultSchemaEntity()); - - // The Sequence and Table SQL should use the PU default schema - assertContainsSQL("ALTER SEQUENCE PUSCHEMA.SeqName_4DefaultSchema"); - assertContainsSQL("INSERT INTO PUSCHEMA.PUDefaultSchemaEntity"); - } - - public void persist(Object ent){ - EntityManager em = emf.createEntityManager(); - EntityTransaction tx = em.getTransaction(); - tx.begin(); - em.persist(ent); - tx.commit(); - em.close(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.pudefaults; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityTransaction; + +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +/* + * OPENJPA-2704: These tests expand on TestSchemaPUDefault to verify + * that a schema defined in an orm's persistence-unit-default is + * overriden by the "openjpa.jdbc.Schema" property . + */ +public class TestOpenJPASchemaPUDefault extends SQLListenerTestCase { + + @Override + public void setUp() throws Exception { + super.setUp(PUDefaultSchemaEntity.class, PUSchemaInSequenceAnnotationEntity.class, + PUSchemaInTableAnnotationEntity.class, PUSchemaInTableMappingEntity.class, + PUSchemaInSequenceMappingEntity.class); + setSupportedDatabases(org.apache.openjpa.jdbc.sql.DB2Dictionary.class); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + } + + @Override + protected String getPersistenceUnitName() { + return "overrideMappingSchema"; + } + + public void testOpenJPASchemaOverridesORM() { + persist(new PUDefaultSchemaEntity()); + + // The Sequence and Table SQL should use the PU default schema + assertContainsSQL("ALTER SEQUENCE PUSCHEMA.SeqName_4DefaultSchema"); + assertContainsSQL("INSERT INTO PUSCHEMA.PUDefaultSchemaEntity"); + } + + public void persist(Object ent){ + EntityManager em = emf.createEntityManager(); + EntityTransaction tx = em.getTransaction(); + tx.begin(); + em.persist(ent); + tx.commit(); + em.close(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/TestSchemaPUDefault.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/TestSchemaPUDefault.java index 1485186b2f..5796773c4c 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/TestSchemaPUDefault.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/pudefaults/TestSchemaPUDefault.java @@ -1,103 +1,103 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.pudefaults; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityTransaction; - -import org.apache.openjpa.persistence.test.SQLListenerTestCase; - -/* - * OPENJPA-2494: This test verifies that a schema defined in an orm's - * persistence-unit-default is used in certain scenarios, and overridden - * in other scenarios. - */ -public class TestSchemaPUDefault extends SQLListenerTestCase { - - @Override - public void setUp() throws Exception { - super.setUp(PUDefaultSchemaEntity.class, PUSchemaInSequenceAnnotationEntity.class, - PUSchemaInTableAnnotationEntity.class, PUSchemaInTableMappingEntity.class, - PUSchemaInSequenceMappingEntity.class); - setSupportedDatabases(org.apache.openjpa.jdbc.sql.DB2Dictionary.class); - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - } - - @Override - protected String getPersistenceUnitName() { - return "puDefault"; - } - - public void testSchemaInPUDefault() { - persist(new PUDefaultSchemaEntity()); - - // The Sequence and Table SQL should use the PU default schema - assertContainsSQL("ALTER SEQUENCE schemaInPUDefaults.SeqName_4DefaultSchema"); - assertContainsSQL("INSERT INTO schemaInPUDefaults.PUDefaultSchemaEntity"); - } - - public void testSchemaInSequenceAnnotation() { - persist(new PUSchemaInSequenceAnnotationEntity()); - - // The Sequence SQL should use the schema defined in the annotation - assertContainsSQL("ALTER SEQUENCE schemaInSequenceAnnotation.SeqName_4AnnoSequenceSchema"); - // The Table SQL should use the schema defined in the PU default schema - assertContainsSQL("INSERT INTO schemaInPUDefaults.PUSchemaInSequenceAnnotationEntity"); - } - - public void testSchemaInTableAnnotation() { - persist(new PUSchemaInTableAnnotationEntity()); - - // The Sequence SQL should use the schema defined in the PU default schema - assertContainsSQL("ALTER SEQUENCE schemaInPUDefaults.SeqName_4AnnoTableSchema"); - // The Table SQL should use the schema defined in the annotation - assertContainsSQL("INSERT INTO schemaInTableAnnotation.PUSchemaInTable"); - } - - public void testSchemaInTableMapping() { - persist(new PUSchemaInTableMappingEntity()); - - // The Sequence SQL should use the schema defined in the PU default schema - assertContainsSQL("ALTER SEQUENCE schemaInPUDefaults.SeqName_4TableMappingSchema"); - // The Table SQL should use the schema defined in the mapping file - assertContainsSQL("INSERT INTO schemaInTableMapping.PUSchemaInTableMapping"); - } - - public void testSchemaInSequenceMapping() { - persist(new PUSchemaInSequenceMappingEntity()); - - // The Sequence SQL should use the schema defined in the mapping file - assertContainsSQL("ALTER SEQUENCE schemaInSequenceMapping.SeqName_4SequenceMappingSchema"); - // The Table SQL should use the schema defined in the PU default schema - assertContainsSQL("INSERT INTO schemaInPUDefaults.PUSchemaInSequenceMappingEntity"); - } - - public void persist(Object ent){ - EntityManager em = emf.createEntityManager(); - EntityTransaction tx = em.getTransaction(); - tx.begin(); - em.persist(ent); - tx.commit(); - em.close(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.pudefaults; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityTransaction; + +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +/* + * OPENJPA-2494: This test verifies that a schema defined in an orm's + * persistence-unit-default is used in certain scenarios, and overridden + * in other scenarios. + */ +public class TestSchemaPUDefault extends SQLListenerTestCase { + + @Override + public void setUp() throws Exception { + super.setUp(PUDefaultSchemaEntity.class, PUSchemaInSequenceAnnotationEntity.class, + PUSchemaInTableAnnotationEntity.class, PUSchemaInTableMappingEntity.class, + PUSchemaInSequenceMappingEntity.class); + setSupportedDatabases(org.apache.openjpa.jdbc.sql.DB2Dictionary.class); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + } + + @Override + protected String getPersistenceUnitName() { + return "puDefault"; + } + + public void testSchemaInPUDefault() { + persist(new PUDefaultSchemaEntity()); + + // The Sequence and Table SQL should use the PU default schema + assertContainsSQL("ALTER SEQUENCE schemaInPUDefaults.SeqName_4DefaultSchema"); + assertContainsSQL("INSERT INTO schemaInPUDefaults.PUDefaultSchemaEntity"); + } + + public void testSchemaInSequenceAnnotation() { + persist(new PUSchemaInSequenceAnnotationEntity()); + + // The Sequence SQL should use the schema defined in the annotation + assertContainsSQL("ALTER SEQUENCE schemaInSequenceAnnotation.SeqName_4AnnoSequenceSchema"); + // The Table SQL should use the schema defined in the PU default schema + assertContainsSQL("INSERT INTO schemaInPUDefaults.PUSchemaInSequenceAnnotationEntity"); + } + + public void testSchemaInTableAnnotation() { + persist(new PUSchemaInTableAnnotationEntity()); + + // The Sequence SQL should use the schema defined in the PU default schema + assertContainsSQL("ALTER SEQUENCE schemaInPUDefaults.SeqName_4AnnoTableSchema"); + // The Table SQL should use the schema defined in the annotation + assertContainsSQL("INSERT INTO schemaInTableAnnotation.PUSchemaInTable"); + } + + public void testSchemaInTableMapping() { + persist(new PUSchemaInTableMappingEntity()); + + // The Sequence SQL should use the schema defined in the PU default schema + assertContainsSQL("ALTER SEQUENCE schemaInPUDefaults.SeqName_4TableMappingSchema"); + // The Table SQL should use the schema defined in the mapping file + assertContainsSQL("INSERT INTO schemaInTableMapping.PUSchemaInTableMapping"); + } + + public void testSchemaInSequenceMapping() { + persist(new PUSchemaInSequenceMappingEntity()); + + // The Sequence SQL should use the schema defined in the mapping file + assertContainsSQL("ALTER SEQUENCE schemaInSequenceMapping.SeqName_4SequenceMappingSchema"); + // The Table SQL should use the schema defined in the PU default schema + assertContainsSQL("INSERT INTO schemaInPUDefaults.PUSchemaInSequenceMappingEntity"); + } + + public void persist(Object ent){ + EntityManager em = emf.createEntityManager(); + EntityTransaction tx = em.getTransaction(); + tx.begin(); + em.persist(ent); + tx.commit(); + em.close(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntityM2O.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntityM2O.java index 21174d033e..a6f85a6c48 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntityM2O.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/QCEntityM2O.java @@ -1,55 +1,55 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.querycache; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; - -@Entity -public class QCEntityM2O { - @Id - @Column(name = "PK") - private String pk; - - @ManyToOne(fetch = FetchType.LAZY) - private QCEntity qc; - - public QCEntityM2O(String pk) { - this.pk = pk; - } - - public String getPk() { - return pk; - } - - public void setPk(String pk) { - this.pk = pk; - } - - public void setQc(QCEntity qc) { - this.qc = qc; - } - - public QCEntity getQc() { - return qc; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.querycache; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; + +@Entity +public class QCEntityM2O { + @Id + @Column(name = "PK") + private String pk; + + @ManyToOne(fetch = FetchType.LAZY) + private QCEntity qc; + + public QCEntityM2O(String pk) { + this.pk = pk; + } + + public String getPk() { + return pk; + } + + public void setPk(String pk) { + this.pk = pk; + } + + public void setQc(QCEntity qc) { + this.qc = qc; + } + + public QCEntity getQc() { + return qc; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/TestQueryCacheWithDataCache.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/TestQueryCacheWithDataCache.java index 720dfc67ef..85d21f5c9c 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/TestQueryCacheWithDataCache.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/querycache/TestQueryCacheWithDataCache.java @@ -1,87 +1,87 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.querycache; - -import java.util.List; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; - -import org.apache.openjpa.persistence.FetchPlan; -import org.apache.openjpa.persistence.OpenJPAQuery; -import org.apache.openjpa.persistence.test.SingleEMFTestCase; - -public class TestQueryCacheWithDataCache extends SingleEMFTestCase { - - @Override - public void setUp() { - super.setUp(DROP_TABLES, QCEntityM2O.class, QCEntity.class, "openjpa.DataCache", "true", - "openjpa.RemoteCommitProvider", "sjvm", "openjpa.QueryCache", "true"); - } - - /* - * Test for OPENJPA-2586 - */ - public void testWithFetchPlan() { - populate(); - - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - doQueryWithFetchPlan(em); - em.getTransaction().commit(); - em.close(); - - em = emf.createEntityManager(); - em.getTransaction().begin(); - doQueryWithFetchPlan(em); - em.getTransaction().commit(); - em.close(); - } - - public void doQueryWithFetchPlan(EntityManager em) { - String jpql = "Select e1 from QCEntityM2O e1"; - - Query q = em.createQuery(jpql); - FetchPlan fetchPlan = q.unwrap(OpenJPAQuery.class).getFetchPlan(); - fetchPlan.addField(QCEntityM2O.class, "qc"); - List results = (List) q.getResultList(); - - em.clear(); - - assertTrue("No results returned!", !results.isEmpty()); - for (QCEntityM2O e1 : results) { - assertNotNull("A 'QCEntity' should have been returned!", e1.getQc()); - } - } - - public void populate() { - EntityManager em = emf.createEntityManager(); - em.getTransaction().begin(); - - QCEntityM2O e1 = new QCEntityM2O("aQCEntityM2O"); - QCEntity e2 = new QCEntity("aQCEntityM2O", "test", 2L); - e1.setQc(e2); - - em.persist(e1); - em.persist(e2); - - em.getTransaction().commit(); - em.close(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.querycache; + +import java.util.List; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; + +import org.apache.openjpa.persistence.FetchPlan; +import org.apache.openjpa.persistence.OpenJPAQuery; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +public class TestQueryCacheWithDataCache extends SingleEMFTestCase { + + @Override + public void setUp() { + super.setUp(DROP_TABLES, QCEntityM2O.class, QCEntity.class, "openjpa.DataCache", "true", + "openjpa.RemoteCommitProvider", "sjvm", "openjpa.QueryCache", "true"); + } + + /* + * Test for OPENJPA-2586 + */ + public void testWithFetchPlan() { + populate(); + + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + doQueryWithFetchPlan(em); + em.getTransaction().commit(); + em.close(); + + em = emf.createEntityManager(); + em.getTransaction().begin(); + doQueryWithFetchPlan(em); + em.getTransaction().commit(); + em.close(); + } + + public void doQueryWithFetchPlan(EntityManager em) { + String jpql = "Select e1 from QCEntityM2O e1"; + + Query q = em.createQuery(jpql); + FetchPlan fetchPlan = q.unwrap(OpenJPAQuery.class).getFetchPlan(); + fetchPlan.addField(QCEntityM2O.class, "qc"); + List results = (List) q.getResultList(); + + em.clear(); + + assertTrue("No results returned!", !results.isEmpty()); + for (QCEntityM2O e1 : results) { + assertNotNull("A 'QCEntity' should have been returned!", e1.getQc()); + } + } + + public void populate() { + EntityManager em = emf.createEntityManager(); + em.getTransaction().begin(); + + QCEntityM2O e1 = new QCEntityM2O("aQCEntityM2O"); + QCEntity e2 = new QCEntity("aQCEntityM2O", "test", 2L); + e1.setQc(e2); + + em.persist(e1); + em.persist(e2); + + em.getTransaction().commit(); + em.close(); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/entity/LrsEntityA.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/entity/LrsEntityA.java index f45a0173f0..cc88f53c93 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/entity/LrsEntityA.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/entity/LrsEntityA.java @@ -1,101 +1,101 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.relations.entity; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; - -import jakarta.persistence.Basic; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; - -import org.apache.openjpa.persistence.LRS; - -@Entity -@Table(name = "LrsEntityB") -public class LrsEntityA implements Serializable { - private static final long serialVersionUID = 1L; - - @Id - @GeneratedValue(strategy=GenerationType.AUTO) - Integer id; - - @Column(length = 30) - String name; - @Basic(fetch = FetchType.LAZY) - int age; - - @LRS - @OneToMany(mappedBy = "entitya", cascade = CascadeType.ALL) - public Collection entitybs; - - public LrsEntityA() { - this.name = "none"; - this.entitybs = new ArrayList<>(); - } - - public LrsEntityA(String nam) { - this.name = nam; - entitybs = new ArrayList<>(); - } - - public LrsEntityA(int id, String nam, int age) { - this.id = id; - this.name = nam; - this.age = age; - entitybs = new ArrayList<>(); - } - - public Collection getEntitybs() { - return entitybs; - } - - public void setEntitybs(Collection entitybs) { - this.entitybs = entitybs; - } - - public int getAge() { - return age; - } - - public void setAge(int age) { - this.age = age; - } - - public int getId() { - return id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.relations.entity; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; + +import jakarta.persistence.Basic; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import org.apache.openjpa.persistence.LRS; + +@Entity +@Table(name = "LrsEntityB") +public class LrsEntityA implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + Integer id; + + @Column(length = 30) + String name; + @Basic(fetch = FetchType.LAZY) + int age; + + @LRS + @OneToMany(mappedBy = "entitya", cascade = CascadeType.ALL) + public Collection entitybs; + + public LrsEntityA() { + this.name = "none"; + this.entitybs = new ArrayList<>(); + } + + public LrsEntityA(String nam) { + this.name = nam; + entitybs = new ArrayList<>(); + } + + public LrsEntityA(int id, String nam, int age) { + this.id = id; + this.name = nam; + this.age = age; + entitybs = new ArrayList<>(); + } + + public Collection getEntitybs() { + return entitybs; + } + + public void setEntitybs(Collection entitybs) { + this.entitybs = entitybs; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/entity/LrsEntityB.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/entity/LrsEntityB.java index a766924f3d..243ec35a94 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/entity/LrsEntityB.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/relations/entity/LrsEntityB.java @@ -1,85 +1,85 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.relations.entity; - -import java.io.Serializable; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; - -@Entity -@Table(name = "LrsEntityB") -public class LrsEntityB implements Serializable { - private static final long serialVersionUID = 1L; - - @Id - @GeneratedValue(strategy=GenerationType.AUTO) - Integer id; - - @Column(length = 30) - String name; - - @ManyToOne() - LrsEntityA entitya; - - public LrsEntityB() { - this.name = "none"; - this.entitya = null; - } - - public LrsEntityB(String nam) { - this.name = nam; - this.entitya = null; - } - - public LrsEntityB(String nam, LrsEntityA entitya) { - this.name = nam; - this.entitya = entitya; - if (entitya != null) - entitya.getEntitybs().add(this); - } - - public LrsEntityA getEntitya() { - return entitya; - } - - public void setEntitya(LrsEntityA entitya) { - this.entitya = entitya; - if (entitya != null) - entitya.getEntitybs().add(this); - } - - public Integer getId() { - return id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.relations.entity; + +import java.io.Serializable; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "LrsEntityB") +public class LrsEntityB implements Serializable { + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + Integer id; + + @Column(length = 30) + String name; + + @ManyToOne() + LrsEntityA entitya; + + public LrsEntityB() { + this.name = "none"; + this.entitya = null; + } + + public LrsEntityB(String nam) { + this.name = nam; + this.entitya = null; + } + + public LrsEntityB(String nam, LrsEntityA entitya) { + this.name = nam; + this.entitya = entitya; + if (entitya != null) + entitya.getEntitybs().add(this); + } + + public LrsEntityA getEntitya() { + return entitya; + } + + public void setEntitya(LrsEntityA entitya) { + this.entitya = entitya; + if (entitya != null) + entitya.getEntitybs().add(this); + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Person.java b/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Person.java index 2f98c722b3..982a2a2f3b 100644 --- a/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Person.java +++ b/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/Person.java @@ -1,101 +1,101 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.lockmgr; - -import java.io.Externalizable; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; -import java.util.List; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToMany; -import jakarta.persistence.Table; - - -@Entity -@Table(name="LOCK_PSN") -public class Person implements Externalizable { - - private int id; - - private String firstName; - private String lastName; - private List phoneNumbers; - - @Id - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - @ManyToMany(mappedBy = "owners") - public List getPhoneNumbers(){ - return phoneNumbers; - } - - public void setPhoneNumbers(List numbers){ - phoneNumbers = numbers; - } - - @Override - public String toString() { - return this.getClass().getName() + '@' - + Integer.toHexString(System.identityHashCode(this)) + "[id=" - + getId() + "] first=" + getFirstName() - + ", last=" + getLastName() + " phone numbers="+phoneNumbers.toString(); - } - - @Override - public void readExternal(ObjectInput in) throws IOException, - ClassNotFoundException { - id = in.readInt(); - firstName = (String) in.readObject(); - lastName = (String) in.readObject(); - phoneNumbers = (List) in.readObject(); - } - - @Override - public void writeExternal(ObjectOutput out) throws IOException { - out.writeInt(id); - out.writeObject(firstName); - out.writeObject(lastName); - out.writeObject(phoneNumbers); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.lockmgr; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.List; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; + + +@Entity +@Table(name="LOCK_PSN") +public class Person implements Externalizable { + + private int id; + + private String firstName; + private String lastName; + private List phoneNumbers; + + @Id + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + @ManyToMany(mappedBy = "owners") + public List getPhoneNumbers(){ + return phoneNumbers; + } + + public void setPhoneNumbers(List numbers){ + phoneNumbers = numbers; + } + + @Override + public String toString() { + return this.getClass().getName() + '@' + + Integer.toHexString(System.identityHashCode(this)) + "[id=" + + getId() + "] first=" + getFirstName() + + ", last=" + getLastName() + " phone numbers="+phoneNumbers.toString(); + } + + @Override + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + id = in.readInt(); + firstName = (String) in.readObject(); + lastName = (String) in.readObject(); + phoneNumbers = (List) in.readObject(); + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(id); + out.writeObject(firstName); + out.writeObject(lastName); + out.writeObject(phoneNumbers); + } +} diff --git a/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/VersionEntity.java b/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/VersionEntity.java index eeafe975c8..3a612c7ae0 100644 --- a/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/VersionEntity.java +++ b/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/VersionEntity.java @@ -1,61 +1,61 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.openjpa.persistence.lockmgr; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import jakarta.persistence.Version; - -@Entity -@Table(name="LK_VERSENT") -public class VersionEntity { - - @Id - private int id; - - private String name; - - @Version - private int version; - - public void setId(int id) { - this.id = id; - } - - public int getId() { - return id; - } - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setVersion(int version) { - this.version = version; - } - - public int getVersion() { - return version; - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.lockmgr; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Version; + +@Entity +@Table(name="LK_VERSENT") +public class VersionEntity { + + @Id + private int id; + + private String name; + + @Version + private int version; + + public void setId(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setVersion(int version) { + this.version = version; + } + + public int getVersion() { + return version; + } +} diff --git a/openjpa-project/pom.xml b/openjpa-project/pom.xml index 1e021de056..79924c5def 100644 --- a/openjpa-project/pom.xml +++ b/openjpa-project/pom.xml @@ -132,6 +132,13 @@ + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + org.apache.maven.plugins maven-assembly-plugin diff --git a/pom.xml b/pom.xml index 41cb23abf5..3ed5e28f3f 100644 --- a/pom.xml +++ b/pom.xml @@ -77,33 +77,33 @@ false 10.15.2.0 - 2.5.1 + 2.7.3 5.1.49 2.7.2 - 42.5.1 + 42.7.3 11.2.1.jre8 - 6.2.0 - 1.7.23 + 6.13.3 + 2.0.16 ${java.class.version} ${java.class.version} ${java.testclass.version} ${java.testclass.version} - 3.0.1 + 3.8.0 - 3.0.0-M7 + 3.4.0 - 4.23 - 3.0.0 - 2.19.0 - 1.10.12 + 4.25 + 3.0.1 + 2.23.1 + 1.10.14 - 2.9.0 - 2.8.0 - 2.9.0 + 2.13.1 + 2.12.0 + 2.12.0 2.1.1 3.0.0 @@ -272,7 +272,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.2.5 @@ -319,15 +319,16 @@ ${maven.javadoc.version} ${project.build.sourceEncoding} - - ${javadoc.additionalparam} -Xdoclint:none - + ${java.class.version} + none + + ${javadoc.additionalparam} + org.apache.openjpa false 1024m - https://download.oracle.com/javase/8/docs/api/ - https://download.oracle.com/javaee/8/api/ + https://docs.oracle.com/en/java/javase/11/docs/api/ @@ -1969,9 +1970,10 @@ ${maven.javadoc.version} ${project.build.sourceEncoding} - - ${javadoc.additionalparam} -Xdoclint:none - + none + + ${javadoc.additionalparam} + @@ -2047,7 +2049,7 @@ io.fabric8 docker-maven-plugin - 0.36.0 + 0.45.0 org.apache.maven.plugins @@ -2062,12 +2064,15 @@ org.codehaus.mojo buildnumber-maven-plugin - 1.4 + 3.2.0 true offline true - {0,date,long} at {0,time,short} + {0,date,long} at {0,time,short} + + timestamp + @@ -2081,17 +2086,17 @@ org.apache.rat apache-rat-plugin - 0.12 + 0.16.1 org.apache.maven.plugins maven-dependency-plugin - 3.1.1 + 3.7.1 org.apache.maven.plugins maven-release-plugin - 2.5.3 + 3.1.1 false @{project.version} @@ -2140,22 +2145,22 @@ org.codehaus.mojo taglist-maven-plugin - 2.4 + 3.1.0 org.apache.maven.plugins maven-shade-plugin - 3.2.0 + 3.6.0 org.apache.maven.plugins maven-checkstyle-plugin - 3.1.1 + 3.4.0 org.apache.maven.plugins maven-jar-plugin - 3.1.0 + 3.4.2 default-jar @@ -2203,7 +2208,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.0.0 + 3.6.0 @@ -2290,7 +2295,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M3 + 3.5.0 validate @@ -2303,7 +2308,7 @@ [3.3.9,) - [1.8,) + [11,) @@ -2364,8 +2369,7 @@ true ${java.class.version} - https://download.oracle.com/javase/8/docs/api/ - https://download.oracle.com/javaee/8/api/ + https://docs.oracle.com/en/java/javase/11/docs/api/