From 10f0818daeae9e454c76edcd3baef7cdf910f48a Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Tue, 28 Jan 2020 17:11:12 +0100 Subject: [PATCH 001/271] Use OpenJDK8 for building --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 47d48a68..b158a224 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: false language: java jdk: - - oraclejdk8 + - openjdk8 cache: directories: - $HOME/.m2 From 16938c858ddbc26034a66a0940cadad33f405e35 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 11 Sep 2021 20:22:09 +0200 Subject: [PATCH 002/271] Migrate to new travis-ci --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb17e509..975a71ee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # GraphStore -[![Build Status](https://travis-ci.org/gephi/graphstore.svg?branch=master)](https://travis-ci.org/gephi/graphstore) +[![Build Status](https://app.travis-ci.com/gephi/graphstore.svg?branch=master)](https://app.travis-ci.com/github/gephi/graphstore) [![Coverage Status](https://coveralls.io/repos/gephi/graphstore/badge.svg?branch=master&service=github)](https://coveralls.io/github/gephi/graphstore?branch=master) GraphStore is an in-memory graph structure implementation written in Java. It is designed to be powerful, efficient and robust. It's powering the Gephi software and supports large graphs in intensive applications. From 48708edf136f629ea02a5325f04d921aaf5a2e01 Mon Sep 17 00:00:00 2001 From: Gerwin Jansen Date: Mon, 13 Sep 2021 22:18:56 +0200 Subject: [PATCH 003/271] Fixes #139 Dynamic weight of an interval always returns zero --- .../java/org/gephi/graph/impl/EdgeImpl.java | 14 +++-- .../org/gephi/graph/impl/EdgeImplTest.java | 59 +++++++++++++++---- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java index fe48a9ac..6b79f8f5 100644 --- a/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -153,7 +153,7 @@ public double getWeight(double timestamp) { } if (dynamicValue instanceof IntervalMap) { - return (Double) ((IntervalMap) dynamicValue) + return (Double) dynamicValue .get(new Interval(timestamp, timestamp), DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); } else { return (Double) dynamicValue.get(timestamp, DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); @@ -174,12 +174,14 @@ public double getWeight(Interval interval) { return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; } - if (dynamicValue instanceof TimestampMap) { - Double doubleVal = (Double) dynamicValue.get(interval, GraphStoreConfiguration.DEFAULT_ESTIMATOR); - return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } else { - return (Double) dynamicValue.get(interval, DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); + Estimator estimator = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) + .getEstimator(); + if (estimator == null) { + estimator = GraphStoreConfiguration.DEFAULT_ESTIMATOR; } + + Double doubleVal = (Double) dynamicValue.get(interval, estimator); + return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; } } diff --git a/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java b/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java index 5be79887..b4147489 100644 --- a/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java +++ b/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java @@ -71,7 +71,7 @@ public void testGetDefaultIntervalWeight() { GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(42.0, new Interval(1.0, 2.0)); - Assert.assertEquals(e.getWeight(new Interval(2.0, 4.0)), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); + Assert.assertEquals(e.getWeight(new Interval(2.1, 4.0)), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); } @Test @@ -84,6 +84,33 @@ public void testGetDefaultIntervalWeightWhenNotSet() { Assert.assertEquals(e.getWeight(new Interval(2.0, 4.0)), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); } + @Test + public void testGetWeightInterval() { + Configuration config = new Configuration(); + config.setTimeRepresentation(TimeRepresentation.INTERVAL); + config.setEdgeWeightType(IntervalDoubleMap.class); + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); + Edge e = graphStore.getEdge("0"); + e.setWeight(42.0, new Interval(1.0, 2.0)); + Assert.assertEquals(e.getWeight(new Interval(2.0, 4.0)), 42.0); + } + + @Test + public void testGetWeightIntervalMax() { + Configuration config = new Configuration(); + config.setTimeRepresentation(TimeRepresentation.INTERVAL); + config.setEdgeWeightType(IntervalDoubleMap.class); + + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); + Column col = graphStore.edgeTable.store.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + col.setEstimator(Estimator.MAX); + + Edge e = graphStore.getEdge("0"); + e.setWeight(10.0, new Interval(1.0, 2.0)); + e.setWeight(20.0, new Interval(2.0, 3.0)); + Assert.assertEquals(e.getWeight(new Interval(2.0, 2.0)), 20.0); + } + @Test public void testSetTimestampWeight() { Configuration config = new Configuration(); @@ -193,18 +220,24 @@ public void testGetTimestampWeightMainGraphView() { Assert.assertEquals(e.getWeight(graphStore.getView()), 10.0); } - // @Test - // public void testGetIntervalWeightMainGraphView() { - // GraphStore graphStore = - // GraphGenerator.generateTinyGraphStore(TimeRepresentation.INTERVAL); - // Edge e = graphStore.getEdge("0"); - // Interval i1 = new Interval(1.0, 2.0); - // Interval i2 = new Interval(3.0, 4.0); - // e.setWeight(42.0, i1); - // Assert.assertEquals(e.getWeight(graphStore.getView()), 42.0); - // e.setWeight(10.0, i2); - // Assert.assertEquals(e.getWeight(graphStore.getView()), 10.0); - // } + @Test + public void testGetWeightGraphViewMax() { + Configuration config = new Configuration(); + config.setTimeRepresentation(TimeRepresentation.INTERVAL); + config.setEdgeWeightType(IntervalDoubleMap.class); + + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); + Column col = graphStore.edgeTable.store.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + col.setEstimator(Estimator.MAX); + + Edge e = graphStore.getEdge("0"); + Interval i1 = new Interval(1.0, 2.0); + Interval i2 = new Interval(3.0, 4.0); + e.setWeight(10.0, i1); + e.setWeight(20.0, i2); + Assert.assertEquals(e.getWeight(graphStore.getView()), 20.0); + } + @Test public void testGetWeightNoValue() { Configuration config = new Configuration(); From ab06257a072a5a3f6aaa4ace04ce393017bcff9b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 26 Oct 2021 18:55:51 +0200 Subject: [PATCH 004/271] Add snapshot repo to the README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 975a71ee..127c5089 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ GraphStore is an in-memory graph structure implementation written in Java. It is Stable releases can be found on [Maven central](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.gephi%22%20AND%20a%3A%22graphstore%22). +Development builds can be found on [Sonatype's Snapshot Repository](https://oss.sonatype.org/content/repositories/snapshots/org/gephi/graphstore/). + ## Documentation API Documentation is available [here](http://gephi.github.com/graphstore/apidocs/index.html). From 0fcf07b549441f15833c3e24d286a1831a483b6d Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 26 Oct 2021 18:56:30 +0200 Subject: [PATCH 005/271] Repair benchmark pom.xml repository [ci skip] --- store-benchmark/pom.xml | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/store-benchmark/pom.xml b/store-benchmark/pom.xml index 4e8ea18d..967761f7 100644 --- a/store-benchmark/pom.xml +++ b/store-benchmark/pom.xml @@ -4,7 +4,7 @@ org.gephi graphstore-benchmark - 0.6.0-SNAPSHOT + 0.6.1-SNAPSHOT jar graphstore-benchmark @@ -12,8 +12,9 @@ UTF-8 + 0.6.0-SNAPSHOT - + @@ -29,14 +30,14 @@ - + org.apache.maven.plugins maven-compiler-plugin - 1.6 - 1.6 + 1.8 + 1.8 @@ -59,7 +60,20 @@ ${project.groupId} graphstore - 0.6.0-SNAPSHOT + ${graphstore.version} + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + true + + + false + + + From 3f999e6740893097e637144b1f49142934881576 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 13 Nov 2021 13:07:46 +0100 Subject: [PATCH 006/271] Create initial ci.yml --- .github/workflows/ci.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..58b623d5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Navigate to the /store folder + run: cd store + - name: Decode GPG keys + run: | + openssl aes-256-cbc -k "$GPG_PUBRING_ENCRYPTION" -in src/travis/pubring.gpg.enc -d -a -out src/travis/pubring.gpg + openssl aes-256-cbc -k "$GPG_SECRETRING_ENCRYPTION" -in src/travis/secretring.gpg.enc -d -a -out src/travis/secretring.gpg + - name: Set up Maven Central Repository + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + server-id: ossrh + server-username: OSSRH_USER + server-password: OSSRH_PASS + - name: Publish package + run: mvn --batch-mode -Djava.awt.headless=true -Dgpg.passphrase=$GPG_PASSPHRASE -Dgpg.defaultKeyring=false -Dgpg-keyname=1481F619 -Dgpg.publicKeyring=src/travis/pubring.gpg -Dgpg.secretKeyring=src/travis/secretring.gpg deploy -P release + env: + OSSRH_USER: ${{ secrets.OSSRH_USER }} + OSSRH_PASS: ${{ secrets.OSSRH_PASS }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_PUBRING_ENCRYPTION: ${{ secrets.GPG_PUBRING_ENCRYPTION }} + GPG_SECRETRING_ENCRYPTION: ${{ secrets.GPG_SECRETRING_ENCRYPTION }} From 56ca2e4dc5449ed2c6318ed13e2aa55ba893a52b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 13 Nov 2021 20:28:02 +0100 Subject: [PATCH 007/271] Set working directory in ci.yml --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58b623d5..1172fa34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,11 +9,11 @@ on: jobs: build: runs-on: ubuntu-latest - + defaults: + run: + working-directory: ./store steps: - uses: actions/checkout@v2 - - name: Navigate to the /store folder - run: cd store - name: Decode GPG keys run: | openssl aes-256-cbc -k "$GPG_PUBRING_ENCRYPTION" -in src/travis/pubring.gpg.enc -d -a -out src/travis/pubring.gpg From ef8fc1e4498109ba2e65bf4578565b9ef8d64d4d Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 13 Nov 2021 21:22:02 +0100 Subject: [PATCH 008/271] Update GPG signing --- .github/workflows/ci.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1172fa34..ed59f3a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,23 +14,20 @@ jobs: working-directory: ./store steps: - uses: actions/checkout@v2 - - name: Decode GPG keys - run: | - openssl aes-256-cbc -k "$GPG_PUBRING_ENCRYPTION" -in src/travis/pubring.gpg.enc -d -a -out src/travis/pubring.gpg - openssl aes-256-cbc -k "$GPG_SECRETRING_ENCRYPTION" -in src/travis/secretring.gpg.enc -d -a -out src/travis/secretring.gpg - name: Set up Maven Central Repository uses: actions/setup-java@v2 with: java-version: '11' - distribution: 'adopt' + distribution: 'temurin' + cache: 'maven' server-id: ossrh server-username: OSSRH_USER server-password: OSSRH_PASS + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} + gpg-passphrase: GPG_PASSPHRASE - name: Publish package - run: mvn --batch-mode -Djava.awt.headless=true -Dgpg.passphrase=$GPG_PASSPHRASE -Dgpg.defaultKeyring=false -Dgpg-keyname=1481F619 -Dgpg.publicKeyring=src/travis/pubring.gpg -Dgpg.secretKeyring=src/travis/secretring.gpg deploy -P release + run: mvn -B -Djava.awt.headless=true deploy -P release env: OSSRH_USER: ${{ secrets.OSSRH_USER }} OSSRH_PASS: ${{ secrets.OSSRH_PASS }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - GPG_PUBRING_ENCRYPTION: ${{ secrets.GPG_PUBRING_ENCRYPTION }} - GPG_SECRETRING_ENCRYPTION: ${{ secrets.GPG_SECRETRING_ENCRYPTION }} + GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} From 44fdb32c3d27a2ee7667b177359b64eb6d940a46 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 13 Nov 2021 21:25:20 +0100 Subject: [PATCH 009/271] Fix GPG passphrase env in ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed59f3a7..3be2602e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,4 +30,4 @@ jobs: env: OSSRH_USER: ${{ secrets.OSSRH_USER }} OSSRH_PASS: ${{ secrets.OSSRH_PASS }} - GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} From 595e23c2c535999bd559bdbb7823538562d70b00 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 13 Nov 2021 21:30:39 +0100 Subject: [PATCH 010/271] Add GPG arguments to pom.xml --- store/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/store/pom.xml b/store/pom.xml index 61749d8e..20ea7f06 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -271,6 +271,12 @@ + + + --pinentry-mode + loopback + + From 55576528f9abcfc4d9f0cfd95e1a30fab5993d19 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 13 Nov 2021 22:00:15 +0100 Subject: [PATCH 011/271] Update .gitignore with IntelliJ files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d12f9eaf..a4ccad2a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ nbactions.xml /store-benchmark/target/ /store/graphstore-api/target/ /store/graphstore/target/ +.idea +*.iml From 76f6a9de511e3136416ce07c676ac6d155d97b9a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Nov 2021 10:04:38 +0100 Subject: [PATCH 012/271] Update dependencies and plugins in POM --- store/pom.xml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/store/pom.xml b/store/pom.xml index 20ea7f06..ce030e7e 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -50,8 +50,8 @@ UTF-8 UTF-8 - 1.6 - 1.6 + 1.8 + 1.8 github @@ -59,13 +59,13 @@ org.testng testng - 6.11 + 6.14.3 test it.unimi.dsi fastutil - 8.1.0 + 8.3.0 colt @@ -75,7 +75,7 @@ joda-time joda-time - 2.9.9 + 2.10.3 @@ -85,12 +85,12 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.8.1 org.apache.maven.plugins maven-surefire-plugin - 2.20.1 + 2.22.2 false @@ -98,12 +98,12 @@ org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.1.0 org.apache.maven.plugins maven-javadoc-plugin - 2.10.4 + 3.1.1 org.apache.maven.plugins @@ -118,7 +118,7 @@ org.jacoco jacoco-maven-plugin - 0.7.9 + 0.8.4 org.eluder.coveralls @@ -133,7 +133,7 @@ org.codehaus.mojo animal-sniffer-maven-plugin - 1.16 + 1.18 net.revelc.code @@ -218,7 +218,7 @@ org.codehaus.mojo.signature - java16 + java18 1.0 @@ -271,12 +271,12 @@ - + --pinentry-mode loopback - + From ce90d18f6fbd1db372b06443597efee9a579afb6 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Nov 2021 10:16:13 +0100 Subject: [PATCH 013/271] Remove Travis [skip ci] --- .travis.yml | 17 --------- README.md | 2 +- store/src/travis/pubring.gpg.enc | 26 -------------- store/src/travis/secretring.gpg.enc | 55 ----------------------------- 4 files changed, 1 insertion(+), 99 deletions(-) delete mode 100644 .travis.yml delete mode 100644 store/src/travis/pubring.gpg.enc delete mode 100644 store/src/travis/secretring.gpg.enc diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b158a224..00000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -sudo: false -language: java -jdk: - - openjdk8 -cache: - directories: - - $HOME/.m2 -before_install: - - cd store - - openssl aes-256-cbc -k "$GPG_PUBRING_ENCRYPTION" -in src/travis/pubring.gpg.enc -d -a -out src/travis/pubring.gpg - - openssl aes-256-cbc -k "$GPG_SECRETRING_ENCRYPTION" -in src/travis/secretring.gpg.enc -d -a -out src/travis/secretring.gpg -install: - - echo "ossrh\${env.OSSRH_USER}\${env.OSSRH_PASS}deploymenttrue\${env.GPG_PASSPHRASE}" > ~/settings.xml -script: - - mvn --settings ~/settings.xml -Djava.awt.headless=true -Dgpg.defaultKeyring=false -Dgpg-keyname=1481F619 -Dgpg.publicKeyring=src/travis/pubring.gpg -Dgpg.secretKeyring=src/travis/secretring.gpg clean deploy -P release -after_success: - - mvn clean test jacoco:report coveralls:report diff --git a/README.md b/README.md index 127c5089..7b6a4445 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # GraphStore -[![Build Status](https://app.travis-ci.com/gephi/graphstore.svg?branch=master)](https://app.travis-ci.com/github/gephi/graphstore) +[![build](https://github.com/gephi/graphstore/actions/workflows/ci.yml/badge.svg)](https://github.com/gephi/graphstore/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/gephi/graphstore/badge.svg?branch=master&service=github)](https://coveralls.io/github/gephi/graphstore?branch=master) GraphStore is an in-memory graph structure implementation written in Java. It is designed to be powerful, efficient and robust. It's powering the Gephi software and supports large graphs in intensive applications. diff --git a/store/src/travis/pubring.gpg.enc b/store/src/travis/pubring.gpg.enc deleted file mode 100644 index 85e73bf7..00000000 --- a/store/src/travis/pubring.gpg.enc +++ /dev/null @@ -1,26 +0,0 @@ -U2FsdGVkX18g16na409X0Wa6FPFPgFyiT1HWT4D0MdDKVOnTvTGDPYxJms3aFmn1 -VZBbYpuHgjFsWUK9Zd8WA3MDyzcCAQ9HZ0KuZGJnFeZHKl5gcT3P3IoEw4E2ZQn6 -7G0GBYMDnVc3Hf4fy5U2QXoUFCwuYePryYdLAhpT6LacscJ2NFnpa3U9UDvnLDOQ -EXUl6nisXgqs4Hrs4n+Fp3vTXHCEDaUqIhI65XgR6oZNJD/iVeveDIk3AD4B5w6+ -6EjWU3Hsr1x3mZTdO8hxpb+IqxNzkyE2eYe+XF5LBDG6wTxlvB2lDp52VGmiWlC6 -WKq8jF3oUuXWNwCvvdTa5bRiYFIK3JrnidZFURZBC0K3+tlS3RDSUeqKytxdjHUU -VNehXZvACxodhKtKx4GfsDGt6aWmHSlJlBjg6DnnPNhIt+Es9/DAJRRG+KK4ob0L -tLgDy8aSgw2avNztpCwXL6GIG7yhrbVNJcCkL8zaljyp9fjqithvxFRa0PGP2b2Z -4n5ek8/n9a/uDTE+FuiMz+GCw3rx2YMfKIIrkISmPvVg4vT2nGmV4c8T9CCK7JZE -vI7g510PAJ8EDMYDCuBhCONeEJhK+odrZhXDEZ+tKwUkdnSWioI/OFdywc4kb53Z -lpAKJjJXUi54SwrCLH5oPmfGfUwupd3KLIjMDmRWhvL/r7Qsbs6AEgKtkwyPwwLn -r8KiLHVPFgnbkatXRM4teNX2aSwhnAJ8LhBA073fcg5+WRlUOtWMmAISDGCA2qtA -kzV0C9etAcmUc/7wb1TuiIF0FPR/YeiSorajXCSMH393Rh61IfxExG2CRZZpvN+1 -aFJoQIrNEibDSJRU8tRivMdB0BB56fyvm9SwVAOUKhdBg1h0LTM/0Ee147I3xLq8 -CDX2KHDSbegPF9yV+seauDNhF7ZQlAQA95J1ktxiwtouJWi4ZfaSCWezDxo8bA24 -qJq+1T5dgyECEXx85VIxJU4RcEQJC4FN+KaAGZxwOz1M+zRaKZTPbzDadq26j+PI -zrBxQ8Xxbcip1JDQ9TRLT7NVfTwvoIwAAm6GjYQH6bCXbxplH60cmFQQJHlklf92 -kGQxmOyqcIvFl+nzWDcaiVbTw/rLvmMmtwcENskx8O+L7SCBU2s7RDHXDwb5TeM/ -92xaulFivlF6brqIhQO6XHoVulLYZ3y1s/1igxKfsgwcnSjpYFrLq8XwxKA+h9NH -KJMuT4gf5IqH21SXt1gr1MquBxSlXSACyTWX1MaHIGuegKLIYIwDzXyZyCmtDjcq -Rc/xkrKQ+g9JVTD1oMJC5Srifp7WoLVRpj2pq2RsWaA6Ys+xGM27Gcoumb9MRYdH -3PGjeXzf7fbrFKm9tgQ47vU54iLJaGzN93buQe7DNLe7a9IjFJfu2Hw/0zxbVMmp -JLw1CYCzHWtzG08HGXEHJ31oQxsm9eGJSBYwVVYi1SS37vye8eYd7JT7H8NBck15 -k8s8lrCf8V1yhUEVqv5RQys8uha9gqYA5egRq8ZkX/Pqcqdo+ZZfvM2+jsNrukDG -oTH1ix676EWN082MQuB/YCTd2uDmRF+4DSa8DvH7SPgx0e5cQYx7q53wyGmw6vrF -04qRL0KmOG59GMQ9qOGL/Q== diff --git a/store/src/travis/secretring.gpg.enc b/store/src/travis/secretring.gpg.enc deleted file mode 100644 index 41b749b0..00000000 --- a/store/src/travis/secretring.gpg.enc +++ /dev/null @@ -1,55 +0,0 @@ -U2FsdGVkX1+cuX4jSQeTw8x9YJgTSrhQxzABFFHS5OSdgrKi+JgDGjreaQe+SSQ4 -hgyZ+TO3mLSKnNsXOnSooQzWmi7VjXWEl7BR0k+s5DB5bGxzrmwvmU0wMrJbOm0k -7vQJGgkZn/wn6RSA33pbEKW3NYWN5oSt4ZsYVP5ecYF/feAm7dJ5ZAncSAMQw2Gy -71qWuKQcVHqlhM/My4hw40aO2dLN79Y/Wzi5hRhxndBX2kio4QPwxpib/zw4jz4v -1+lhzlUuAXQdnPp90Cbq7kn48wxVhat+aP6cwO6NTvzlRw0fgdjfKRgm9J42zMf1 -BroUPCMMQclEnEqDcW8Ti7DgP818YY/h3ghPYMu14upfpCxXzUeI5RGnGR9syfyn -7xMur6fCF31BCOoz/BsLsUys+sxB2TcDEpk+6/sbomkhvvM/LOYtGpGitqLOzzWm -Yv3mMUpb2ls5GEiPC10iTyryhhhryLc9wtHMawOYpz2u5GL9+B5n0Wcem8FjKpze -WGwTlXoCDH3kcxWkFz6up4mWIM6/zl3mkUFqy+S961Iv3EVxTwypql2Rn0e6l5RO -2ilheg9ikJ/psehjtydRMqndG3eXCPLZYV/iMwST4DboGnDckcqnLlsThIQkA473 -4jGJLDPxDVCU3jBnQcRqi0bd4eExfTrV5sTgGaTeIyn/QSSz5RV7Nfc2QEXoz9EN -ZZwdTNnXfrD/y5HXO71SKKQtSsTUE2rxHBBr0x7oAZCzaOys2wpww2qEyvMYZ+H+ -3NZZs3nyKJXOPeJ20G4uLgnOp9nNhv06UPrUfMPtX8HygSLqg0/naiI2rc3wPrjC -rFzZNM7QblPRATWU9239coPHPmSlTfB6KqwtOS34BAG8jnRqjH2zzZO7P6pNf8xk -RHHe9pbeIwpfHnul+x7mHzVP2gGNKInEYR5ekeDndlHEa9SVI5nVxXcoTBpDQdZz -rywsMgcPxps1wbz1nRnjbQm7mbo5fL/b06V62nSSu/pHzKHzQgc1AoOtL1N+t1l1 -l8y3KCl0T5/MQgAfMO7HuGmmfZfZ5VibmMH4H8GOdRxQzV+OyQ5xsLZCsi1BTXAW -UzGIrKURmnBwI8937jct6Vr0LOVfN0G6QuBS/IDwzyfpIYZYVAR65JWeeekTVpDL -LVxIVBQb9ETLwROYBOwfzSw2bZNNueYEOG9wWZqnNOlsf29OOsSi4dalIBf7wBfd -lAjS3PZgt2v5/RXIKZ6pXP+ZDWUl6Fea7Mpy0a0P5v/93qUrk0ssO2CCv3qYZKds -wzR3KzKVifMrPvZn4pl3fQvNe91tKQ1jX/cYbqntXY2H2Yz+4ZmFQpFqYVpupz/3 -/X89JNUl6B6izAo6T8DlrFxQPlKZN8vxAAKxXXb95YOObEvc/8yYVXSVzzG/XZrs -wOek4MGBoshJuUOw1GByG89Jvbjaq8wjJ+3SfcygsMYL1AUySoOllWzfxZjNK8yN -b7RP5YK4MpVH5vTRhCXM64GpaRpGsJ4Ovz2bdr9bg00DK8MRGWo58cM25ApP1p40 -rmr78yLQ0HnVztERwp81Uo1SskyGxTR+Txs7ylztuezRG4bHFJ4sI18SKBgjg0xf -ukQ2prSXP3U9nBPfpJovOPAbp9mUc03u1W0aPV9X+dOmGVvAon/ZKm7/VyLRmpk4 -3jUbUO9OnxsF7Nvpc0FwT1ns4cYPBJo0W/3Xt62oZAkQ9Qll6xWhsxaSJslwhddN -skgAyWJfx/LcTrpFWXNpSH7et7SMz6poqmhOQbIq4gmP4MVRrq2mch+5vBqjsG40 -Sl/Tx8yiX3unl4ugin9iLeZ0kBJ0OQj+99KRyTsLmBUDb320bgHSp4QKD8WgKJoM -tu6U39trNpWYvtT5L9vESP2fwyNR/xF4d/W3Og/IH2CZvd62Bl+K7maC3D0Y7LcB -K5U2tHG+EXmttZ+pLomvU+1X0tkRzEtw9DC6jaAkvPPPqjf68zfBg2YkbZlhrH9l -PNrjMj96o9XOvZtnQhkwfm8PFtgpcQ+L6UjWjrwZNia9bj+gdxWpm3odunQlzMvq -ZclFgwi0nIDxSPq/Soag4ZqSa6ow2EkbGd6gXUnWJFgiEDCykzWDDp1zX6CX8Eoz -h5DVIqJUTFfvldE2HyOJoXwmeai/MYyyk8EJiMoluycen2zbL9QHBtbA0Gx5Yjr8 -iQpqBeJiRsiPnVubbLquIQQ/vEElAlm6wkqrtlTHUP5726RIYesZpAmdChPtuse6 -7ch/V37Sbc0tkWLBdcb9tHbo5mrIVNK9Fz7f/NpBHc7ONK8LSQFfwrRkFsiaTq8l -R7SmJo+Wo/5aeS/6hK10+oWLGipt1a8rmdUri+x16tICY7mlg5hm6voeqG13Iif5 -rBmQAieeNo5jjBE1Hrovu9Uv1kh/LSKOdRj3r0XFfTVuj+uiE667rW6AsRT/f/Hk -l+gUfh0/BV6Wqw/U5GUIQykEcUM1msGq7z1US+C35dkWG8t7YqEPtxPVEeuej/wg -B8u8jPhiTvYWszz+ELMTvDIIqlw0XKGxO1eDVM2S2K/0dDDZwC3CaBgGq3br+vcK -PTzXVeDSKYICEAseF5307DmyMh4wpIhXANU3sblRLJUow/ySVgRw/UAQ8yLNZmr0 -kljKrRDohZ674sli1l++T5Q2DZ4zUTWSddXeW8B1mTRumkjwFyLSrxh4MoAjIDuT -k5Jjr/q8hXGgoFlglCx4K/sNKDl3JqpTSwT3VMTxU6A4cyowEuy947OXFsqektmJ -44+nXzv9L2CAZvGT98wGA7FekAzYTWHicIJAF3CZTfNVobU7wCSISE+oZS1pmeWj -af0/8ktNNR2pt3j3b3Fwxm9oeGPJoD+7QRwrQkSJKkWFhgZsiorcon7y3rDmWfUM -TNkgpNjvZVh7p/y5vougAcTf/hAIp2Jd5U2flXQOcDy47u7c8uX10ldH1WdYAWlW -uVSjN7TaZhQSRC84V75ET0MpAUwPVXXTLbwAZW+TjLcCl4zlK07rFogadxlFfYGB -c6KVQ42eSy1HByNLD31U8kM3MO7F0snuvyXyKxzh2wFT6lsriqGDsneVomb7Fe1M -FehhgOksVc1+YDqbh7GMH+U5jjBm7pTlnKD8uHalNlutixUTrrMP8EAf14zjf9YJ -KqdG4ZyW7eb++OiajgtQDIZGIry8v1TCLTeX4RRcRPHyaF5sKduMM5XOpJ3pxhX5 -miyP11m/jhMHCGZwQ934PRV7/NosNKC9MLHHI6cpMjjV1/+IoYNAfKo7+SIM1MdW -fKvv162rGhl880fnqlTVdlDCJhxumXHakM/t2yb4/oQ2JRm5n9BdRsZs8oGJ3wAP -rlHa3fmHH0HB3dVws3ZkzAyXVe5GzRe2t4XG8uoQdUrIiiAO6eOKtlw6pxHsidJP -XUQ5QDDH8PbcuWgqxhDyL2AKu/ODlwz3DUWRxgxtJYbF6TulSlS9t6nnUtxO3Xu9 -HLSC/M5EDpnHfEbxkOrJyQ== From 00e3a02b6c02ac5970f6cbf860b14a927afe903b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Nov 2021 10:53:37 +0100 Subject: [PATCH 014/271] Update coveralls report so it's included in GH actions --- .github/workflows/ci.yml | 2 ++ store/pom.xml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3be2602e..90d3f766 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,8 @@ jobs: gpg-passphrase: GPG_PASSPHRASE - name: Publish package run: mvn -B -Djava.awt.headless=true deploy -P release + - name: Submit test coverage to Coveralls + run: mvn test jacoco:report coveralls:report -DrepoToken=${{ secrets.COVERALLS_TOKEN }} env: OSSRH_USER: ${{ secrets.OSSRH_USER }} OSSRH_PASS: ${{ secrets.OSSRH_PASS }} diff --git a/store/pom.xml b/store/pom.xml index ce030e7e..3d1c8f63 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -118,7 +118,7 @@ org.jacoco jacoco-maven-plugin - 0.8.4 + 0.8.6 org.eluder.coveralls @@ -211,7 +211,7 @@ coveralls-maven-plugin - + org.codehaus.mojo animal-sniffer-maven-plugin From 5aab6f3b1f99c6fd9895e3104d737d67ae0bd312 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Nov 2021 11:01:59 +0100 Subject: [PATCH 015/271] Remove build cache for now --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90d3f766..f8557a3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,6 @@ jobs: with: java-version: '11' distribution: 'temurin' - cache: 'maven' server-id: ossrh server-username: OSSRH_USER server-password: OSSRH_PASS From 64019da596e7d644c433d0dc7b4954be02221a2f Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Nov 2021 11:22:53 +0100 Subject: [PATCH 016/271] Fix env variables on ci.yml --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8557a3c..2bdb7de8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,9 +26,9 @@ jobs: gpg-passphrase: GPG_PASSPHRASE - name: Publish package run: mvn -B -Djava.awt.headless=true deploy -P release - - name: Submit test coverage to Coveralls - run: mvn test jacoco:report coveralls:report -DrepoToken=${{ secrets.COVERALLS_TOKEN }} env: OSSRH_USER: ${{ secrets.OSSRH_USER }} OSSRH_PASS: ${{ secrets.OSSRH_PASS }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + - name: Submit test coverage to Coveralls + run: mvn test jacoco:report coveralls:report -DrepoToken=${{ secrets.COVERALLS_TOKEN }} From e37e1f5f6f068bf6be3d26e565783dee61fe2952 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Nov 2021 11:27:05 +0100 Subject: [PATCH 017/271] Fix issue with coveralls plugin --- store/pom.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/store/pom.xml b/store/pom.xml index 3d1c8f63..5cf07073 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -124,6 +124,14 @@ org.eluder.coveralls coveralls-maven-plugin 4.3.0 + + + + javax.xml.bind + jaxb-api + 2.3.1 + + com.github.github From a46ae90481f1d82166f508353a90cb312561cbab Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Nov 2021 13:11:47 +0100 Subject: [PATCH 018/271] Remove support for gh-pages site [skip ci] --- README.md | 2 +- store/pom.xml | 40 ---------------------------------------- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/README.md b/README.md index 7b6a4445..fa5b290f 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Development builds can be found on [Sonatype's Snapshot Repository](https://oss. ## Documentation -API Documentation is available [here](http://gephi.github.com/graphstore/apidocs/index.html). +API Documentation is available [here](https://www.javadoc.io/doc/org.gephi/graphstore/latest/index.html). ## Dependencies diff --git a/store/pom.xml b/store/pom.xml index 5cf07073..496f1392 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -133,11 +133,6 @@ - - com.github.github - site-maven-plugin - 0.12 - org.codehaus.mojo animal-sniffer-maven-plugin @@ -287,24 +282,6 @@ - - - org.apache.maven.plugins - maven-site-plugin - - - default-site - site - - site - - - true - - - - - org.apache.maven.plugins @@ -321,23 +298,6 @@ true - - - - com.github.github - site-maven-plugin - - Creating site for ${project.version} - - - - - site - - site - - - From a6a5ead0c1f0eaf69ef74744ffdaf04dfb78de96 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Nov 2021 13:59:18 +0100 Subject: [PATCH 019/271] Add workflows for PRs --- .github/workflows/ci.yml | 2 -- .github/workflows/pr.yml | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pr.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2bdb7de8..57f4d60e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,6 @@ name: CI on: push: branches: [ master ] - pull_request: - branches: [ master ] jobs: build: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..0471851f --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,20 @@ +name: PR + +on: + pull_request: + +jobs: + build_and_test: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./store + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'temurin' + - name: Build project with Maven + run: mvn -B package --file pom.xml From f1db9726bc089fd5672f2b044b2a8d74c051f421 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 14 Nov 2021 14:02:56 +0100 Subject: [PATCH 020/271] Add a few utility methods --- .../java/org/gephi/graph/api/GraphModel.java | 17 +++++++++++++++++ .../main/java/org/gephi/graph/api/Table.java | 14 ++++++++++++++ .../org/gephi/graph/impl/GraphModelImpl.java | 16 ++++++++++++++++ .../java/org/gephi/graph/impl/TableImpl.java | 12 ++++++++++++ 4 files changed, 59 insertions(+) diff --git a/store/src/main/java/org/gephi/graph/api/GraphModel.java b/store/src/main/java/org/gephi/graph/api/GraphModel.java index 493f064d..b3640b00 100644 --- a/store/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/store/src/main/java/org/gephi/graph/api/GraphModel.java @@ -463,6 +463,23 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce */ public Index getEdgeIndex(GraphView view); + /** + * Gets the node or edge index depending on the column provided. + * + * @param table the table to get the index for + * @return element index, either node or edge + */ + public Index getElementIndex(Table table); + + /** + * Gets the node or edge index for the given graph view. + * + * @param table the table to get the index for + * @param view the view to get the index from + * @return edge index + */ + public Index getElementIndex(Table table, GraphView view); + /** * Gets the node time index. * diff --git a/store/src/main/java/org/gephi/graph/api/Table.java b/store/src/main/java/org/gephi/graph/api/Table.java index 3112c428..24767117 100644 --- a/store/src/main/java/org/gephi/graph/api/Table.java +++ b/store/src/main/java/org/gephi/graph/api/Table.java @@ -130,4 +130,18 @@ public interface Table extends ColumnIterable { * @return graph */ public Graph getGraph(); + + /** + * Returns true if this table is the node table. + * + * @return true if node table, false otherwise + */ + public boolean isNodeTable(); + + /** + * Returns true if this table is the node table. + * + * @return true if node table, false otherwise + */ + public boolean isEdgeTable(); } diff --git a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index b39903db..bf5c9b1f 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -16,6 +16,7 @@ package org.gephi.graph.impl; import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.Element; import org.gephi.graph.api.Index; import org.gephi.graph.api.Table; import org.gephi.graph.api.TimeFormat; @@ -285,6 +286,21 @@ public Index getNodeIndex(GraphView view) { return null; } + @Override + public Index getElementIndex(Table table) { + return getElementIndex(table, store.mainGraphView); + } + + @Override + public Index getElementIndex(Table table, GraphView view) { + if (table.isNodeTable()) { + return getNodeIndex(view); + } else if (table.isEdgeTable()) { + return getEdgeIndex(view); + } + return null; + } + @Override public Index getEdgeIndex() { return getEdgeIndex(store.mainGraphView); diff --git a/store/src/main/java/org/gephi/graph/impl/TableImpl.java b/store/src/main/java/org/gephi/graph/impl/TableImpl.java index 2d724fe0..e3944aef 100644 --- a/store/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -19,6 +19,8 @@ import java.util.List; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Table; import org.gephi.graph.api.TableObserver; @@ -145,6 +147,16 @@ public Graph getGraph() { return store.graphStore; } + @Override + public boolean isNodeTable() { + return Node.class.equals(store.elementType); + } + + @Override + public boolean isEdgeTable() { + return Edge.class.equals(store.elementType); + } + public void destroyTableObserver(TableObserver observer) { checkableTableObserver(observer); From 270f01c551dc072b7a013f1a923c21df4e6de4c4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 13:38:08 +0100 Subject: [PATCH 021/271] Refactor quadtree implementation and add tests --- store/pom.xml | 2 +- .../main/java/org/gephi/graph/api/Graph.java | 2 - .../java/org/gephi/graph/api/GraphModel.java | 7 + ...{SpatialContext.java => SpatialIndex.java} | 2 +- .../java/org/gephi/graph/impl/EdgeImpl.java | 1072 ++++++++--------- .../gephi/graph/impl/EdgeIterableWrapper.java | 21 + .../java/org/gephi/graph/impl/EdgeStore.java | 16 +- .../org/gephi/graph/impl/EdgesQuadTree.java | 666 ---------- .../graph/impl/ElementIterableWrapper.java | 48 + .../org/gephi/graph/impl/GraphModelImpl.java | 6 + .../java/org/gephi/graph/impl/GraphStore.java | 114 +- .../graph/impl/GraphStoreConfiguration.java | 2 + .../impl/GraphStoreSpatialContextImpl.java | 235 ---- .../gephi/graph/impl/GraphViewDecorator.java | 13 +- .../java/org/gephi/graph/impl/NodeImpl.java | 17 + .../gephi/graph/impl/NodeIterableWrapper.java | 21 + .../java/org/gephi/graph/impl/NodeStore.java | 12 +- .../org/gephi/graph/impl/NodesQuadTree.java | 242 ++-- .../gephi/graph/impl/SpatialIndexImpl.java | 131 ++ .../gephi/graph/impl/SpatialNodeDataImpl.java | 26 + .../gephi/graph/impl/UndirectedDecorator.java | 7 +- .../org/gephi/graph/impl/BasicGraphStore.java | 7 +- .../org/gephi/graph/impl/GraphGenerator.java | 10 + .../gephi/graph/impl/NodesQuadTreeTest.java | 139 ++- .../graph/impl/SpatialIndexImplTest.java | 84 ++ 25 files changed, 1158 insertions(+), 1744 deletions(-) rename store/src/main/java/org/gephi/graph/api/{SpatialContext.java => SpatialIndex.java} (92%) create mode 100644 store/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java delete mode 100644 store/src/main/java/org/gephi/graph/impl/EdgesQuadTree.java create mode 100644 store/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java delete mode 100644 store/src/main/java/org/gephi/graph/impl/GraphStoreSpatialContextImpl.java create mode 100644 store/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java create mode 100644 store/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java create mode 100644 store/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java create mode 100644 store/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java diff --git a/store/pom.xml b/store/pom.xml index 176f69b2..496f1392 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.1-SNAPSHOT + 0.6.0-SNAPSHOT jar GraphStore diff --git a/store/src/main/java/org/gephi/graph/api/Graph.java b/store/src/main/java/org/gephi/graph/api/Graph.java index 5209c1dc..e2e698e7 100644 --- a/store/src/main/java/org/gephi/graph/api/Graph.java +++ b/store/src/main/java/org/gephi/graph/api/Graph.java @@ -498,6 +498,4 @@ public interface Graph { * Closes a write lock for the current thread. */ public void writeUnlock(); - - public SpatialContext getSpatialContext(); } diff --git a/store/src/main/java/org/gephi/graph/api/GraphModel.java b/store/src/main/java/org/gephi/graph/api/GraphModel.java index b3640b00..b76bcf89 100644 --- a/store/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/store/src/main/java/org/gephi/graph/api/GraphModel.java @@ -551,6 +551,13 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce */ public GraphObserver createGraphObserver(Graph graph, boolean withGraphDiff); + /** + * Returns the spatial index. + * + * @return spatial index + */ + public SpatialIndex getSpatialIndex(); + /** * Returns the time format used to display time. * diff --git a/store/src/main/java/org/gephi/graph/api/SpatialContext.java b/store/src/main/java/org/gephi/graph/api/SpatialIndex.java similarity index 92% rename from store/src/main/java/org/gephi/graph/api/SpatialContext.java rename to store/src/main/java/org/gephi/graph/api/SpatialIndex.java index fbd0266d..bbd5e976 100644 --- a/store/src/main/java/org/gephi/graph/api/SpatialContext.java +++ b/store/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -7,7 +7,7 @@ * * @author Eduardo Ramos */ -public interface SpatialContext { +public interface SpatialIndex { NodeIterable getNodesInArea(Rect2D rect); diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java index 3e0706af..fc66aebc 100644 --- a/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -1,536 +1,536 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.impl; - -import java.awt.Color; -import java.util.Map; -import org.gephi.graph.api.Column; -import org.gephi.graph.api.Estimator; -import org.gephi.graph.api.Interval; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.EdgeProperties; -import org.gephi.graph.api.GraphView; -import org.gephi.graph.api.Table; -import org.gephi.graph.api.types.IntervalMap; -import org.gephi.graph.api.types.TimeMap; -import org.gephi.graph.api.types.TimestampMap; -import static org.gephi.graph.impl.GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - -public class EdgeImpl extends ElementImpl implements Edge { - - // Const - protected static final byte DIRECTED_BYTE = 1; - protected static final byte MUTUAL_BYTE = 1 << 1; - // Final Data - protected final NodeImpl source; - protected final NodeImpl target; - protected final int type; - // Pointers - protected int storeId = EdgeStore.NULL_ID; - protected int nextOutEdge = EdgeStore.NULL_ID; - protected int nextInEdge = EdgeStore.NULL_ID; - protected int previousOutEdge = EdgeStore.NULL_ID; - protected int previousInEdge = EdgeStore.NULL_ID; - // Flags - protected byte flags; - // Props - protected final EdgePropertiesImpl properties; - - public EdgeImpl(Object id, GraphStore graphStore, NodeImpl source, NodeImpl target, int type, double weight, boolean directed) { - super(id, graphStore); - checkIdType(id); - this.source = source; - this.target = target; - this.flags = (byte) (directed ? 1 : 0); - this.type = type; - this.properties = GraphStoreConfiguration.ENABLE_EDGE_PROPERTIES ? new EdgePropertiesImpl() : null; - this.attributes = new Object[GraphStoreConfiguration.EDGE_WEIGHT_INDEX + 1]; - this.attributes[GraphStoreConfiguration.ELEMENT_ID_INDEX] = id; - if (graphStore == null || graphStore.configuration.getEdgeWeightType().equals(Double.class)) { - this.attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX] = weight; - } - } - - public EdgeImpl(Object id, NodeImpl source, NodeImpl target, int type, double weight, boolean directed) { - this(id, null, source, target, type, weight, directed); - } - - @Override - public NodeImpl getSource() { - return source; - } - - @Override - public NodeImpl getTarget() { - return target; - } - - @Override - public double getWeight() { - synchronized (this) { - Object weightObject = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - if (weightObject instanceof Double) { - return (Double) weightObject; - } else { - return getWeight(graphStore.getView()); - } - } - } - - @Override - public boolean hasDynamicWeight() { - return !Double.class.equals(graphStore.configuration.getEdgeWeightType()); - } - - @Override - public void setWeight(double weight, double timestamp) { - checkTimeRepresentationTimestamp(); - setTimeWeight(weight, timestamp); - } - - @Override - public void setWeight(double weight, Interval interval) { - checkTimeRepresentationInterval(); - setTimeWeight(weight, interval); - } - - private void setTimeWeight(double weight, Object timeObject) { - checkWeightDynamicType(); - - boolean res; - synchronized (this) { - Object oldValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - TimeMap dynamicValue = null; - if (oldValue == null) { - try { - attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX] = dynamicValue = (TimeMap) graphStore.configuration - .getEdgeWeightType().newInstance(); - } catch (InstantiationException | IllegalAccessException ex) { - throw new RuntimeException(ex); - } - } else { - dynamicValue = (TimeMap) oldValue; - } - res = dynamicValue.put(timeObject, weight); - } - TimeIndexStore timeIndexStore = getTimeIndexStore(); - if (res && timeIndexStore != null && isValid()) { - timeIndexStore.add(timeObject); - } - ColumnStore columnStore = getColumnStore(); - if (res && columnStore != null && isValid()) { - Column column = columnStore.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); - ((ColumnImpl) column).incrementVersion(this); - } - } - - @Override - public double getWeight(double timestamp) { - synchronized (this) { - Object weightValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - if (weightValue instanceof Double) { - throw new IllegalStateException("The weight is static, call getWeight() instead"); - } - - TimeMap dynamicValue = (TimeMap) weightValue; - if (dynamicValue == null) { - return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } - - if (dynamicValue instanceof IntervalMap) { - return (Double) ((IntervalMap) dynamicValue) - .get(new Interval(timestamp, timestamp), DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); - } else { - return (Double) dynamicValue.get(timestamp, DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); - } - } - } - - @Override - public double getWeight(Interval interval) { - synchronized (this) { - Object weightValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - if (weightValue instanceof Double) { - throw new IllegalStateException("The weight is static, call getWeight() instead"); - } - - TimeMap dynamicValue = (TimeMap) weightValue; - if (dynamicValue == null) { - return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } - - Estimator estimator = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) - .getEstimator(); - if (estimator == null) { - estimator = GraphStoreConfiguration.DEFAULT_ESTIMATOR; - } - - Double doubleVal = (Double) dynamicValue.get(interval, estimator); - return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } - } - - @Override - public double getWeight(GraphView view) { - synchronized (this) { - Object value = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - if (value instanceof TimeMap) { - Interval interval = view.getTimeInterval(); - checkViewExist((GraphView) view); - - TimeMap dynamicValue = (TimeMap) value; - Estimator estimator = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) - .getEstimator(); - if (estimator == null) { - estimator = GraphStoreConfiguration.DEFAULT_ESTIMATOR; - } - - Double doubleVal = (Double) dynamicValue.get(interval, estimator); - return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } else if (value == null) { - return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } else { - // Must be double - return (Double) value; - } - } - } - - @Override - public Iterable getWeights() { - synchronized (this) { - Object weightValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - if (weightValue instanceof Double) { - throw new IllegalStateException("The weight is static, call getWeight() instead"); - } - TimeMap dynamicValue = (TimeMap) weightValue; - Object[] values = dynamicValue.toValuesArray(); - if (dynamicValue instanceof TimestampMap) { - return new TimeAttributeIterable(((TimestampMap) dynamicValue).getTimestamps(), values); - } else if (dynamicValue instanceof IntervalMap) { - return new TimeAttributeIterable(((IntervalMap) dynamicValue).toKeysArray(), values); - } - } - return TimeAttributeIterable.EMPTY_ITERABLE; - } - - @Override - public int getType() { - return type; - } - - @Override - public Object getTypeLabel() { - graphStore.autoReadLock(); - try { - return graphStore.edgeTypeStore.getLabel(type); - } finally { - graphStore.autoReadUnlock(); - } - } - - @Override - public void setWeight(double weight) { - checkWeightStaticType(); - - final Object oldValue; - synchronized (this) { - oldValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX] = weight; - } - - ColumnStore columnStore = getColumnStore(); - if (columnStore != null && isValid()) { - Column column = columnStore.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); - ((ColumnImpl) column).incrementVersion(this); - if (column.isIndexed()) { - columnStore.indexStore.set(column, oldValue, weight, this); - } - } - } - - public int getNextOutEdge() { - return nextOutEdge; - } - - public int getNextInEdge() { - return nextInEdge; - } - - public int getPreviousOutEdge() { - return previousOutEdge; - } - - public int getPreviousInEdge() { - return previousInEdge; - } - - @Override - public int getStoreId() { - return storeId; - } - - public void setStoreId(int id) { - this.storeId = id; - } - - public long getLongId() { - return EdgeStore.getLongId(source, target, isDirected()); - } - - @Override - public boolean isDirected() { - return (flags & DIRECTED_BYTE) == 1; - } - - protected void setMutual(boolean mutual) { - if (isDirected()) { - if (mutual) { - flags |= MUTUAL_BYTE; - } else { - flags &= ~MUTUAL_BYTE; - } - } - } - - protected boolean isMutual() { - return (flags & MUTUAL_BYTE) == MUTUAL_BYTE; - } - - @Override - public boolean isSelfLoop() { - return source == target; - } - - @Override - public Table getTable() { - if (graphStore != null) { - return graphStore.edgeTable; - } - return null; - } - - @Override - ColumnStore getColumnStore() { - if (graphStore != null) { - return graphStore.edgeTable.store; - } - return null; - } - - @Override - TimeIndexStore getTimeIndexStore() { - if (graphStore != null) { - return graphStore.timeStore.edgeIndexStore; - } - return null; - } - - @Override - boolean isValid() { - return storeId != EdgeStore.NULL_ID; - } - - @Override - public float r() { - return properties.r(); - } - - @Override - public float g() { - return properties.g(); - } - - @Override - public float b() { - return properties.b(); - } - - @Override - public float alpha() { - return properties.alpha(); - } - - @Override - public TextPropertiesImpl getTextProperties() { - return properties.getTextProperties(); - } - - protected void setEdgeProperties(EdgePropertiesImpl edgeProperties) { - properties.rgba = edgeProperties.rgba; - if (properties.textProperties != null) { - properties.setTextProperties(edgeProperties.textProperties); - } - } - - @Override - public int getRGBA() { - return properties.rgba; - } - - @Override - public Color getColor() { - return properties.getColor(); - } - - @Override - public void setR(float r) { - properties.setR(r); - } - - @Override - public void setG(float g) { - properties.setG(g); - } - - @Override - public void setB(float b) { - properties.setB(b); - } - - @Override - public void setAlpha(float a) { - properties.setAlpha(a); - } - - @Override - public void setColor(Color color) { - properties.setColor(color); - } - - final void checkIdType(Object id) { - if (graphStore != null && !id.getClass().equals(graphStore.configuration.getEdgeIdType())) { - throw new IllegalArgumentException( - "The id class does not match with the expected type (" + graphStore.configuration.getEdgeIdType() - .getName() + ")"); - } - } - - final void checkWeightStaticType() { - if (graphStore != null && !Double.class.equals(graphStore.configuration.getEdgeWeightType())) { - throw new IllegalArgumentException( - "The weight class does not match with the expected type (" + graphStore.configuration - .getEdgeWeightType().getName() + ")"); - } - } - - final void checkWeightDynamicType() { - if (graphStore != null && Double.class.equals(graphStore.configuration.getEdgeWeightType())) { - throw new IllegalArgumentException( - "The weight class does not match with the expected type (" + graphStore.configuration - .getEdgeWeightType().getName() + ")"); - } - } - - protected static class EdgePropertiesImpl implements EdgeProperties { - - protected final TextPropertiesImpl textProperties; - protected int rgba; - - public EdgePropertiesImpl() { - textProperties = new TextPropertiesImpl(); - this.rgba = 255 << 24; // Alpha set to 1 - } - - @Override - public float r() { - return ((rgba >> 16) & 0xFF) / 255f; - } - - @Override - public float g() { - return ((rgba >> 8) & 0xFF) / 255f; - } - - @Override - public float b() { - return (rgba & 0xFF) / 255f; - } - - @Override - public float alpha() { - return ((rgba >> 24) & 0xFF) / 255f; - } - - @Override - public int getRGBA() { - return rgba; - } - - @Override - public TextPropertiesImpl getTextProperties() { - return textProperties; - } - - protected void setTextProperties(TextPropertiesImpl textProperties) { - this.textProperties.rgba = textProperties.rgba; - this.textProperties.size = textProperties.size; - this.textProperties.text = textProperties.text; - this.textProperties.visible = textProperties.visible; - } - - @Override - public Color getColor() { - return new Color(rgba, true); - } - - @Override - public void setR(float r) { - rgba = (rgba & 0xFF00FFFF) | (((int) (r * 255f)) << 16); - } - - @Override - public void setG(float g) { - rgba = (rgba & 0xFFFF00FF) | ((int) (g * 255f)) << 8; - } - - @Override - public void setB(float b) { - rgba = (rgba & 0xFFFFFF00) | ((int) (b * 255f)); - } - - @Override - public void setAlpha(float a) { - rgba = (rgba & 0xFFFFFF) | ((int) (a * 255f)) << 24; - } - - @Override - public void setColor(Color color) { - rgba = (color.getAlpha() << 24) | color.getRGB(); - } - - public int deepHashCode() { - int hash = 3; - hash = 29 * hash + this.rgba; - hash = 29 * hash + (this.textProperties != null ? this.textProperties.deepHashCode() : 0); - return hash; - } - - public boolean deepEquals(EdgePropertiesImpl obj) { - if (obj == null) { - return false; - } - if (this.rgba != obj.rgba) { - return false; - } - if (this.textProperties != obj.textProperties && (this.textProperties == null || !this.textProperties - .deepEquals(obj.textProperties))) { - return false; - } - return true; - } - } -} +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; + +import java.awt.Color; +import java.util.Map; +import org.gephi.graph.api.Column; +import org.gephi.graph.api.Estimator; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeProperties; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Table; +import org.gephi.graph.api.types.IntervalMap; +import org.gephi.graph.api.types.TimeMap; +import org.gephi.graph.api.types.TimestampMap; +import static org.gephi.graph.impl.GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; + +public class EdgeImpl extends ElementImpl implements Edge { + + // Const + protected static final byte DIRECTED_BYTE = 1; + protected static final byte MUTUAL_BYTE = 1 << 1; + // Final Data + protected final NodeImpl source; + protected final NodeImpl target; + protected final int type; + // Pointers + protected int storeId = EdgeStore.NULL_ID; + protected int nextOutEdge = EdgeStore.NULL_ID; + protected int nextInEdge = EdgeStore.NULL_ID; + protected int previousOutEdge = EdgeStore.NULL_ID; + protected int previousInEdge = EdgeStore.NULL_ID; + // Flags + protected byte flags; + // Props + protected final EdgePropertiesImpl properties; + + public EdgeImpl(Object id, GraphStore graphStore, NodeImpl source, NodeImpl target, int type, double weight, boolean directed) { + super(id, graphStore); + checkIdType(id); + this.source = source; + this.target = target; + this.flags = (byte) (directed ? 1 : 0); + this.type = type; + this.properties = GraphStoreConfiguration.ENABLE_EDGE_PROPERTIES ? new EdgePropertiesImpl() : null; + this.attributes = new Object[GraphStoreConfiguration.EDGE_WEIGHT_INDEX + 1]; + this.attributes[GraphStoreConfiguration.ELEMENT_ID_INDEX] = id; + if (graphStore == null || graphStore.configuration.getEdgeWeightType().equals(Double.class)) { + this.attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX] = weight; + } + } + + public EdgeImpl(Object id, NodeImpl source, NodeImpl target, int type, double weight, boolean directed) { + this(id, null, source, target, type, weight, directed); + } + + @Override + public NodeImpl getSource() { + return source; + } + + @Override + public NodeImpl getTarget() { + return target; + } + + @Override + public double getWeight() { + synchronized (this) { + Object weightObject = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; + if (weightObject instanceof Double) { + return (Double) weightObject; + } else { + return getWeight(graphStore.getView()); + } + } + } + + @Override + public boolean hasDynamicWeight() { + return !Double.class.equals(graphStore.configuration.getEdgeWeightType()); + } + + @Override + public void setWeight(double weight, double timestamp) { + checkTimeRepresentationTimestamp(); + setTimeWeight(weight, timestamp); + } + + @Override + public void setWeight(double weight, Interval interval) { + checkTimeRepresentationInterval(); + setTimeWeight(weight, interval); + } + + private void setTimeWeight(double weight, Object timeObject) { + checkWeightDynamicType(); + + boolean res; + synchronized (this) { + Object oldValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; + TimeMap dynamicValue = null; + if (oldValue == null) { + try { + attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX] = dynamicValue = (TimeMap) graphStore.configuration + .getEdgeWeightType().newInstance(); + } catch (InstantiationException | IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } else { + dynamicValue = (TimeMap) oldValue; + } + res = dynamicValue.put(timeObject, weight); + } + TimeIndexStore timeIndexStore = getTimeIndexStore(); + if (res && timeIndexStore != null && isValid()) { + timeIndexStore.add(timeObject); + } + ColumnStore columnStore = getColumnStore(); + if (res && columnStore != null && isValid()) { + Column column = columnStore.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + ((ColumnImpl) column).incrementVersion(this); + } + } + + @Override + public double getWeight(double timestamp) { + synchronized (this) { + Object weightValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; + if (weightValue instanceof Double) { + throw new IllegalStateException("The weight is static, call getWeight() instead"); + } + + TimeMap dynamicValue = (TimeMap) weightValue; + if (dynamicValue == null) { + return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; + } + + if (dynamicValue instanceof IntervalMap) { + return (Double) ((IntervalMap) dynamicValue) + .get(new Interval(timestamp, timestamp), DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); + } else { + return (Double) dynamicValue.get(timestamp, DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); + } + } + } + + @Override + public double getWeight(Interval interval) { + synchronized (this) { + Object weightValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; + if (weightValue instanceof Double) { + throw new IllegalStateException("The weight is static, call getWeight() instead"); + } + + TimeMap dynamicValue = (TimeMap) weightValue; + if (dynamicValue == null) { + return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; + } + + Estimator estimator = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) + .getEstimator(); + if (estimator == null) { + estimator = GraphStoreConfiguration.DEFAULT_ESTIMATOR; + } + + Double doubleVal = (Double) dynamicValue.get(interval, estimator); + return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; + } + } + + @Override + public double getWeight(GraphView view) { + synchronized (this) { + Object value = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; + if (value instanceof TimeMap) { + Interval interval = view.getTimeInterval(); + checkViewExist((GraphView) view); + + TimeMap dynamicValue = (TimeMap) value; + Estimator estimator = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) + .getEstimator(); + if (estimator == null) { + estimator = GraphStoreConfiguration.DEFAULT_ESTIMATOR; + } + + Double doubleVal = (Double) dynamicValue.get(interval, estimator); + return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; + } else if (value == null) { + return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; + } else { + // Must be double + return (Double) value; + } + } + } + + @Override + public Iterable getWeights() { + synchronized (this) { + Object weightValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; + if (weightValue instanceof Double) { + throw new IllegalStateException("The weight is static, call getWeight() instead"); + } + TimeMap dynamicValue = (TimeMap) weightValue; + Object[] values = dynamicValue.toValuesArray(); + if (dynamicValue instanceof TimestampMap) { + return new TimeAttributeIterable(((TimestampMap) dynamicValue).getTimestamps(), values); + } else if (dynamicValue instanceof IntervalMap) { + return new TimeAttributeIterable(((IntervalMap) dynamicValue).toKeysArray(), values); + } + } + return TimeAttributeIterable.EMPTY_ITERABLE; + } + + @Override + public int getType() { + return type; + } + + @Override + public Object getTypeLabel() { + graphStore.autoReadLock(); + try { + return graphStore.edgeTypeStore.getLabel(type); + } finally { + graphStore.autoReadUnlock(); + } + } + + @Override + public void setWeight(double weight) { + checkWeightStaticType(); + + final Object oldValue; + synchronized (this) { + oldValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; + attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX] = weight; + } + + ColumnStore columnStore = getColumnStore(); + if (columnStore != null && isValid()) { + Column column = columnStore.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + ((ColumnImpl) column).incrementVersion(this); + if (column.isIndexed()) { + columnStore.indexStore.set(column, oldValue, weight, this); + } + } + } + + public int getNextOutEdge() { + return nextOutEdge; + } + + public int getNextInEdge() { + return nextInEdge; + } + + public int getPreviousOutEdge() { + return previousOutEdge; + } + + public int getPreviousInEdge() { + return previousInEdge; + } + + @Override + public int getStoreId() { + return storeId; + } + + public void setStoreId(int id) { + this.storeId = id; + } + + public long getLongId() { + return EdgeStore.getLongId(source, target, isDirected()); + } + + @Override + public boolean isDirected() { + return (flags & DIRECTED_BYTE) == 1; + } + + protected void setMutual(boolean mutual) { + if (isDirected()) { + if (mutual) { + flags |= MUTUAL_BYTE; + } else { + flags &= ~MUTUAL_BYTE; + } + } + } + + protected boolean isMutual() { + return (flags & MUTUAL_BYTE) == MUTUAL_BYTE; + } + + @Override + public boolean isSelfLoop() { + return source == target; + } + + @Override + public Table getTable() { + if (graphStore != null) { + return graphStore.edgeTable; + } + return null; + } + + @Override + ColumnStore getColumnStore() { + if (graphStore != null) { + return graphStore.edgeTable.store; + } + return null; + } + + @Override + TimeIndexStore getTimeIndexStore() { + if (graphStore != null) { + return graphStore.timeStore.edgeIndexStore; + } + return null; + } + + @Override + boolean isValid() { + return storeId != EdgeStore.NULL_ID; + } + + @Override + public float r() { + return properties.r(); + } + + @Override + public float g() { + return properties.g(); + } + + @Override + public float b() { + return properties.b(); + } + + @Override + public float alpha() { + return properties.alpha(); + } + + @Override + public TextPropertiesImpl getTextProperties() { + return properties.getTextProperties(); + } + + protected void setEdgeProperties(EdgePropertiesImpl edgeProperties) { + properties.rgba = edgeProperties.rgba; + if (properties.textProperties != null) { + properties.setTextProperties(edgeProperties.textProperties); + } + } + + @Override + public int getRGBA() { + return properties.rgba; + } + + @Override + public Color getColor() { + return properties.getColor(); + } + + @Override + public void setR(float r) { + properties.setR(r); + } + + @Override + public void setG(float g) { + properties.setG(g); + } + + @Override + public void setB(float b) { + properties.setB(b); + } + + @Override + public void setAlpha(float a) { + properties.setAlpha(a); + } + + @Override + public void setColor(Color color) { + properties.setColor(color); + } + + final void checkIdType(Object id) { + if (graphStore != null && !id.getClass().equals(graphStore.configuration.getEdgeIdType())) { + throw new IllegalArgumentException( + "The id class does not match with the expected type (" + graphStore.configuration.getEdgeIdType() + .getName() + ")"); + } + } + + final void checkWeightStaticType() { + if (graphStore != null && !Double.class.equals(graphStore.configuration.getEdgeWeightType())) { + throw new IllegalArgumentException( + "The weight class does not match with the expected type (" + graphStore.configuration + .getEdgeWeightType().getName() + ")"); + } + } + + final void checkWeightDynamicType() { + if (graphStore != null && Double.class.equals(graphStore.configuration.getEdgeWeightType())) { + throw new IllegalArgumentException( + "The weight class does not match with the expected type (" + graphStore.configuration + .getEdgeWeightType().getName() + ")"); + } + } + + protected static class EdgePropertiesImpl implements EdgeProperties { + + protected final TextPropertiesImpl textProperties; + protected int rgba; + + public EdgePropertiesImpl() { + textProperties = new TextPropertiesImpl(); + this.rgba = 255 << 24; // Alpha set to 1 + } + + @Override + public float r() { + return ((rgba >> 16) & 0xFF) / 255f; + } + + @Override + public float g() { + return ((rgba >> 8) & 0xFF) / 255f; + } + + @Override + public float b() { + return (rgba & 0xFF) / 255f; + } + + @Override + public float alpha() { + return ((rgba >> 24) & 0xFF) / 255f; + } + + @Override + public int getRGBA() { + return rgba; + } + + @Override + public TextPropertiesImpl getTextProperties() { + return textProperties; + } + + protected void setTextProperties(TextPropertiesImpl textProperties) { + this.textProperties.rgba = textProperties.rgba; + this.textProperties.size = textProperties.size; + this.textProperties.text = textProperties.text; + this.textProperties.visible = textProperties.visible; + } + + @Override + public Color getColor() { + return new Color(rgba, true); + } + + @Override + public void setR(float r) { + rgba = (rgba & 0xFF00FFFF) | (((int) (r * 255f)) << 16); + } + + @Override + public void setG(float g) { + rgba = (rgba & 0xFFFF00FF) | ((int) (g * 255f)) << 8; + } + + @Override + public void setB(float b) { + rgba = (rgba & 0xFFFFFF00) | ((int) (b * 255f)); + } + + @Override + public void setAlpha(float a) { + rgba = (rgba & 0xFFFFFF) | ((int) (a * 255f)) << 24; + } + + @Override + public void setColor(Color color) { + rgba = (color.getAlpha() << 24) | color.getRGB(); + } + + public int deepHashCode() { + int hash = 3; + hash = 29 * hash + this.rgba; + hash = 29 * hash + (this.textProperties != null ? this.textProperties.deepHashCode() : 0); + return hash; + } + + public boolean deepEquals(EdgePropertiesImpl obj) { + if (obj == null) { + return false; + } + if (this.rgba != obj.rgba) { + return false; + } + if (this.textProperties != obj.textProperties && (this.textProperties == null || !this.textProperties + .deepEquals(obj.textProperties))) { + return false; + } + return true; + } + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java b/store/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java new file mode 100644 index 00000000..f8e58753 --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java @@ -0,0 +1,21 @@ +package org.gephi.graph.impl; + +import java.util.Iterator; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeIterable; + +public class EdgeIterableWrapper extends ElementIterableWrapper implements EdgeIterable { + + public EdgeIterableWrapper(Iterator iterator) { + super(iterator); + } + + public EdgeIterableWrapper(Iterator iterator, GraphLock lock) { + super(iterator, lock); + } + + @Override + public Edge[] toArray() { + return toArray(new Edge[0]); + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeStore.java b/store/src/main/java/org/gephi/graph/impl/EdgeStore.java index 36df694c..a0fad2c6 100644 --- a/store/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/store/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -61,7 +61,7 @@ public class EdgeStore implements Collection, EdgeIterable { protected final GraphViewStore viewStore; // Spatial index - protected final GraphStoreSpatialContextImpl spatialIndex; + protected final SpatialIndexImpl spatialIndex; public EdgeStore() { initStore(); @@ -72,7 +72,7 @@ public EdgeStore() { this.spatialIndex = null; } - public EdgeStore(final EdgeTypeStore edgeTypeStore, final GraphStoreSpatialContextImpl spatialIndex, final GraphLock lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { + public EdgeStore(final EdgeTypeStore edgeTypeStore, final SpatialIndexImpl spatialIndex, final GraphLock lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { initStore(); this.lock = lock; this.edgeTypeStore = edgeTypeStore; @@ -291,10 +291,6 @@ public void clear() { edge.setStoreId(EdgeStore.NULL_ID); } - if (this.spatialIndex != null) { - this.spatialIndex.clearEdges(); - } - initStore(); } @@ -629,10 +625,6 @@ public boolean add(final Edge e) { undirectedSize++; } - if (this.spatialIndex != null) { - this.spatialIndex.addEdge(e); - } - size++; return true; } else if (isValidIndex(edge.storeId) && get(edge.storeId) == edge) { @@ -657,10 +649,6 @@ public boolean remove(final Object o) { viewStore.removeEdge(edge); } - if (this.spatialIndex != null) { - this.spatialIndex.removeEdge(edge); - } - edge.clearAttributes(); int storeIndex = id / GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE; diff --git a/store/src/main/java/org/gephi/graph/impl/EdgesQuadTree.java b/store/src/main/java/org/gephi/graph/impl/EdgesQuadTree.java deleted file mode 100644 index a531dca8..00000000 --- a/store/src/main/java/org/gephi/graph/impl/EdgesQuadTree.java +++ /dev/null @@ -1,666 +0,0 @@ -package org.gephi.graph.impl; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Deque; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Consumer; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.EdgeIterable; -import org.gephi.graph.api.Rect2D; - -/** - * Adapted from https://bitbucket.org/C3/quadtree/wiki/Home - * - * TODO: unit tests!! - * TODO: almost the same as NodesQuadTree, maybe generate code with templating-maven-plugin - * @author Eduardo Ramos - */ -public class EdgesQuadTree { - - private static final int MAX_OBJECTS_PER_NODE = 2; - private static final int MAX_LEVELS = 16; - - private final GraphLock lock = new GraphLock(); - private Map wrappedDictionary = new LinkedHashMap<>(); - - private QuadTreeNode quadTreeRoot; - - public EdgesQuadTree(Rect2D rect) { - quadTreeRoot = new QuadTreeNode(rect); - } - - public EdgesQuadTree(float dimensionMax) { - this(-dimensionMax, -dimensionMax, dimensionMax, dimensionMax); - } - - public EdgesQuadTree(float minX, float minY, float maxX, float maxY) { - quadTreeRoot = new QuadTreeNode(new Rect2D(minX, minY, maxX, maxY)); - } - - public Rect2D quadRect() { - return quadTreeRoot.quadRect(); - } - - public EdgeIterable getEdges(Rect2D rect) { - return quadTreeRoot.getEdges(rect); - } - - public void getEdges(Rect2D rect, Consumer callback) { - quadTreeRoot.getEdges(rect, callback); - } - - public EdgeIterable getEdges(float minX, float minY, float maxX, float maxY) { - return quadTreeRoot.getEdges(new Rect2D(minX, minY, maxX, maxY)); - } - - public void getEdges(float minX, float minY, float maxX, float maxY, Consumer callback) { - quadTreeRoot.getEdges(new Rect2D(minX, minY, maxX, maxY), callback); - } - - public EdgeIterable getAllEdges() { - return quadTreeRoot.getAllEdges(); - } - - public void getAllEdges(Consumer callback) { - quadTreeRoot.getAllEdges(callback); - } - - public boolean updateEdge(Edge item, float minX, float minY, float maxX, float maxY) { - writeLock(); - try { - final QuadTreeObject obj = wrappedDictionary.get(item); - if (obj != null) { - obj.updateItemCoords(minX, minY, maxX, maxY); - quadTreeRoot.update(obj); - return true; - } else { - return false; - } - } finally { - writeUnlock(); - } - } - - public boolean addEdge(Edge item, float minX, float minY, float maxX, float maxY) { - writeLock(); - try { - if (!containsEdge(item)) { - final QuadTreeObject wrappedObject = new QuadTreeObject(item, minX, minY, maxX, maxY); - wrappedDictionary.put(item, wrappedObject); - quadTreeRoot.insert(wrappedObject); - return true; - } else { - return false; - } - } finally { - writeUnlock(); - } - } - - public void clear() { - writeLock(); - try { - wrappedDictionary.clear(); - quadTreeRoot.clear(); - } finally { - writeUnlock(); - } - } - - public boolean containsEdge(Edge item) { - readLock(); - try { - return wrappedDictionary.containsKey(item); - } finally { - readUnlock(); - } - } - - public int count() { - readLock(); - try { - return wrappedDictionary.size(); - } finally { - readUnlock(); - } - } - - public boolean removeEdge(Edge item) { - writeLock(); - try { - final QuadTreeObject obj = wrappedDictionary.get(item); - if (obj != null) { - quadTreeRoot.delete(obj, true); - wrappedDictionary.remove(item); - return true; - } else { - return false; - } - } finally { - writeUnlock(); - } - } - - public void readLock() { - if (lock != null) { - lock.readLock(); - } - } - - public void readUnlock() { - if (lock != null) { - lock.readUnlock(); - } - } - - public void writeLock() { - if (lock != null) { - lock.writeLock(); - } - } - - public void writeUnlock() { - if (lock != null) { - lock.writeUnlock(); - } - } - - private class QuadTreeObject { - - private final Edge data; - private float minX, minY, maxX, maxY; - - private QuadTreeNode owner; - - public QuadTreeObject(Edge data, float minX, float minY, float maxX, float maxY) { - this.data = data; - updateItemCoords(minX, minY, maxX, maxY); - } - - private void updateItemCoords(float minX, float minY, float maxX, float maxY) { - this.minX = minX; - this.minY = minY; - this.maxX = maxX; - this.maxY = maxY; - } - } - - private class QuadTreeNode { - - private Set objects = null; - private final Rect2D rect; // The area this QuadTree represents - - private final QuadTreeNode parent; // The parent of this quad - private final int level; - - private QuadTreeNode childTL = null; // Top Left Child - private QuadTreeNode childTR = null; // Top Right Child - private QuadTreeNode childBL = null; // Bottom Left Child - private QuadTreeNode childBR = null; // Bottom Right Child - - public Rect2D quadRect() { - return rect; - } - - public QuadTreeNode topLeftChild() { - return childTL; - } - - public QuadTreeNode topRightChild() { - return childTR; - } - - public QuadTreeNode bottomLeftChild() { - return childBL; - } - - public QuadTreeNode bottomRightChild() { - return childBR; - } - - public QuadTreeNode parent() { - return parent; - } - - public int count() { - return objectCount(); - } - - public boolean isEmptyLeaf() { - return count() == 0 && childTL == null; - } - - public QuadTreeNode(Rect2D rect) { - this(null, 0, rect); - } - - private QuadTreeNode(QuadTreeNode parent, int level, Rect2D rect) { - this.level = level; - this.rect = rect; - this.parent = parent; - } - - private void add(QuadTreeObject item) { - if (objects == null) { - objects = new LinkedHashSet<>(); - } - - item.owner = this; - objects.add(item); - } - - private void remove(QuadTreeObject item) { - if (objects != null) { - objects.remove(item); - } - } - - private int objectCount() { - int count = 0; - - // add the objects at this level - if (objects != null) { - count += objects.size(); - } - - // add the objects that are contained in the children - if (childTL != null) { - count += childTL.objectCount(); - count += childTR.objectCount(); - count += childBL.objectCount(); - count += childBR.objectCount(); - } - - return count; - } - - private void subdivide() { - // We've reached capacity, subdivide... - final float minX = rect.minX; - final float halfX = (rect.minX + rect.maxX) / 2; - final float maxX = rect.maxX; - - final float minY = rect.minY; - final float halfY = (rect.minY + rect.maxY) / 2; - final float maxY = rect.maxY; - - childTL = new QuadTreeNode(this, level + 1, new Rect2D(minX, minY, halfX, halfY)); - childTR = new QuadTreeNode(this, level + 1, new Rect2D(halfX, minY, maxX, halfY)); - childBL = new QuadTreeNode(this, level + 1, new Rect2D(minX, halfY, halfX, maxY)); - childBR = new QuadTreeNode(this, level + 1, new Rect2D(halfX, halfY, maxX, maxY)); - - // If they're completely contained by the quad, bump objects down - final Iterator iterator = objects.iterator(); - while (iterator.hasNext()) { - QuadTreeObject obj = iterator.next(); - QuadTreeNode destTree = getDestinationTree(obj); - if (destTree != this) { - // Insert to the appropriate tree, remove the object, and - // back up one in the loop - destTree.insert(obj); - - iterator.remove(); - } - } - } - - private QuadTreeNode getDestinationTree(QuadTreeObject item) { - // If a child can't contain an object, it will live in this Quad - final QuadTreeNode destTree; - - final float minX = item.minX; - final float minY = item.minY; - final float maxX = item.maxX; - final float maxY = item.maxY; - - if (childTL.quadRect().contains(minX, minY, maxX, maxY)) { - destTree = childTL; - } else if (childTR.quadRect().contains(minX, minY, maxX, maxY)) { - destTree = childTR; - } else if (childBL.quadRect().contains(minX, minY, maxX, maxY)) { - destTree = childBL; - } else if (childBR.quadRect().contains(minX, minY, maxX, maxY)) { - destTree = childBR; - } else { - destTree = this; - } - - return destTree; - } - - private void relocate(QuadTreeObject item) { - // Are we still inside our parent? - if (quadRect().contains(item.minX, item.minY, item.maxX, item.maxY)) { - // Good, have we moved inside any of our children? - if (childTL != null) { - QuadTreeNode dest = getDestinationTree(item); - if (item.owner != dest) { - // Delete the item from this quad and add it to our - // child - // Note: Do NOT clean during this call, it can - // potentially delete our destination quad - QuadTreeNode formerOwner = item.owner; - delete(item, false); - dest.insert(item); - - // Clean up ourselves - formerOwner.cleanUpwards(); - } - } - } else { - // We don't fit here anymore, move up, if we can - if (parent != null) { - parent.relocate(item); - } - } - } - - private void cleanUpwards() { - if (childTL != null) { - // If all the children are empty leaves, delete all the children - if (childTL.isEmptyLeaf() && childTR.isEmptyLeaf() && childBL.isEmptyLeaf() && childBR.isEmptyLeaf()) { - childTL = null; - childTR = null; - childBL = null; - childBR = null; - - if (parent != null && count() == 0) { - parent.cleanUpwards(); - } - } - } else { - // I could be one of 4 empty leaves, tell my parent to clean up - if (parent != null && count() == 0) { - parent.cleanUpwards(); - } - } - } - - private void clear() { - // clear out the children, if we have any - if (childTL != null) { - childTL.clear(); - childTR.clear(); - childBL.clear(); - childBR.clear(); - } - - // clear any objects at this level - if (objects != null) { - objects.clear(); - objects = null; - } - - // Set the children to null - childTL = null; - childTR = null; - childBL = null; - childBR = null; - } - - private void delete(QuadTreeObject item, boolean clean) { - if (item.owner != null) { - if (item.owner == this) { - remove(item); - if (clean) { - cleanUpwards(); - } - } else { - item.owner.delete(item, clean); - } - } - } - - private void insert(QuadTreeObject item) { - // If this quad doesn't contain the items rectangle, do nothing, - // unless we are the root - if (!rect.contains(item.minX, item.minY, item.maxX, item.maxY)) { - if (parent == null) { - // This object is outside of the QuadTreeXNA bounds, we - // should add it at the root level - add(item); - } else { - throw new IllegalStateException( - "We are not the root, and this object doesn't fit here. How did we get here?"); - } - } - - if (objects == null || (childTL == null && (level >= MAX_LEVELS || objects.size() + 1 <= MAX_OBJECTS_PER_NODE))) { - // If there's room to add the object, just add it - add(item); - } else { - // No quads, create them and bump objects down where appropriate - if (childTL == null) { - subdivide(); - } - - // Find out which tree this object should go in and add it there - final QuadTreeNode destTree = getDestinationTree(item); - if (destTree == this) { - add(item); - } else { - destTree.insert(item); - } - } - } - - private EdgeIterable getEdges(Rect2D searchRect) { - return new QuadTreeEdgesIterable(searchRect); - } - - private EdgeIterable getAllEdges() { - return new QuadTreeEdgesIterable(null); - } - - private void getEdges(Rect2D searchRect, Consumer callback) { - if (searchRect.contains(this.rect)) { - this.getAllEdges(callback); - } else if (searchRect.intersects(this.rect)) { - if (objects != null && !objects.isEmpty()) { - for (QuadTreeObject obj : objects) { - if (searchRect.intersects(obj.minX, obj.minY, obj.maxX, obj.maxY)) { - callback.accept(obj.data); - } - } - } - - if (childTL != null) { - childTL.getEdges(searchRect, callback); - childTR.getEdges(searchRect, callback); - childBL.getEdges(searchRect, callback); - childBR.getEdges(searchRect, callback); - } - } - } - - private void getAllEdges(Consumer callback) { - if (objects != null && !objects.isEmpty()) { - for (QuadTreeObject obj : objects) { - callback.accept(obj.data); - } - } - - if (childTL != null) { - childTL.getAllEdges(callback); - childTR.getAllEdges(callback); - childBL.getAllEdges(callback); - childBR.getAllEdges(callback); - } - } - - private void update(QuadTreeObject item) { - if (item.owner != null) { - item.owner.relocate(item); - } else { - relocate(item); - } - } - - public void toString(StringBuilder sb) { - for (int i = 0; i < level; i++) { - sb.append(" "); - } - sb.append(rect.toString()).append('\n'); - - if (objects != null) { - for (QuadTreeObject object : objects) { - for (int i = 0; i <= level; i++) { - sb.append(" "); - } - - sb.append(object.data.getId()).append('\n'); - } - } - - if (childTL != null) { - childTL.toString(sb); - childTR.toString(sb); - childBL.toString(sb); - childBR.toString(sb); - } - } - } - - private class QuadTreeEdgesIterable implements EdgeIterable { - - private final Rect2D searchRect; - - public QuadTreeEdgesIterable(Rect2D searchRect) { - this.searchRect = searchRect; - } - - @Override - public Iterator iterator() { - return new QuadTreeEdgesIterator(quadTreeRoot, searchRect); - } - - @Override - public Edge[] toArray() { - final Collection collection = toCollection(); - return collection.toArray(new Edge[collection.size()]); - } - - @Override - public Collection toCollection() { - final List list = new ArrayList<>(); - - final Iterator iterator = iterator(); - while (iterator.hasNext()) { - list.add(iterator.next()); - } - - return list; - } - - @Override - public void doBreak() { - readUnlock(); - } - - } - - private class QuadTreeEdgesIterator implements Iterator { - - private final Rect2D searchRect; - private final Deque nodesStack = new ArrayDeque<>(); - private final Deque fullyContainedStack = new ArrayDeque<>(); - - // Current: - private Iterator currentIterator; - private boolean currentFullyContained = false; - private boolean finished = false; - - private QuadTreeObject next; - - public QuadTreeEdgesIterator(QuadTreeNode root, Rect2D searchRect) { - this.searchRect = searchRect; - - readLock(); - - // Null rect means get all - currentFullyContained = searchRect == null; - - // We always add the root and don't test for the root being fully - // contained, to correctly handle the case of nodes out of the quad - // tree bounds - addChildrenToVisit(root, currentFullyContained); - currentIterator = root.objects != null ? root.objects.iterator() : null; - } - - private void addChildrenToVisit(QuadTreeNode quadTreeNode, boolean fullyContained) { - if (quadTreeNode.childTL != null) { - nodesStack.push(quadTreeNode.childBR); - nodesStack.push(quadTreeNode.childBL); - nodesStack.push(quadTreeNode.childTR); - nodesStack.push(quadTreeNode.childTL); - - fullyContainedStack.push(fullyContained); - fullyContainedStack.push(fullyContained); - fullyContainedStack.push(fullyContained); - fullyContainedStack.push(fullyContained); - } - } - - @Override - public boolean hasNext() { - if (finished) { - return false; - } - - if (next != null) { - return true; - } - - while (currentIterator != null || !nodesStack.isEmpty()) { - if (currentIterator != null) { - while (currentIterator.hasNext()) { - final QuadTreeObject elem = currentIterator.next(); - - if (currentFullyContained || searchRect.intersects(elem.minX, elem.minY, elem.maxX, elem.maxY)) { - next = elem; - return true; - } - } - - currentIterator = null; - } else { - final QuadTreeNode pointer = nodesStack.pop(); - - currentFullyContained = fullyContainedStack.pop() || searchRect.contains(pointer.rect); - - if (currentFullyContained || pointer.rect.intersects(searchRect)) { - addChildrenToVisit(pointer, currentFullyContained); - currentIterator = pointer.objects != null ? pointer.objects.iterator() : null; - } else { - currentIterator = null; - } - } - } - - readUnlock(); - finished = true; - return false; - } - - @Override - public Edge next() { - if (next == null) { - throw new IllegalStateException("No next available!"); - } - - final Edge edge = next.data; - - next = null; - - return edge; - } - - } -} diff --git a/store/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java b/store/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java new file mode 100644 index 00000000..bcfd31af --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java @@ -0,0 +1,48 @@ +package org.gephi.graph.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import org.gephi.graph.api.Element; +import org.gephi.graph.api.ElementIterable; + +public abstract class ElementIterableWrapper implements ElementIterable { + + protected final Iterator iterator; + protected final GraphLock lock; + + public ElementIterableWrapper(Iterator iterator) { + this(iterator, null); + } + + public ElementIterableWrapper(Iterator iterator, GraphLock lock) { + this.iterator = iterator; + this.lock = lock; + } + + @Override + public Iterator iterator() { + return iterator; + } + + protected T[] toArray(T[] a) { + return toCollection().toArray(a); + } + + @Override + public Collection toCollection() { + List list = new ArrayList<>(); + for (; iterator.hasNext();) { + list.add(iterator.next()); + } + return list; + } + + @Override + public void doBreak() { + if (lock != null) { + lock.readUnlock(); + } + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 115b5683..bf267070 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -19,6 +19,7 @@ import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Element; import org.gephi.graph.api.Index; +import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.Table; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.api.Interval; @@ -353,6 +354,11 @@ public TimeIndex getEdgeTimeIndex(GraphView view) { return null; } + @Override + public SpatialIndex getSpatialIndex() { + return store.spatialIndex; + } + @Override public GraphObserver createGraphObserver(Graph graph, boolean withGraphDiff) { store.autoWriteLock(); diff --git a/store/src/main/java/org/gephi/graph/impl/GraphStore.java b/store/src/main/java/org/gephi/graph/impl/GraphStore.java index 41ac5f5d..5e1cd885 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -35,7 +35,6 @@ import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.SpatialContext; import org.gephi.graph.api.Subgraph; import org.joda.time.DateTimeZone; import org.gephi.graph.api.TimeRepresentation; @@ -70,7 +69,7 @@ public class GraphStore implements DirectedGraph, DirectedSubgraph { // Time zone protected DateTimeZone timeZone; // Spatial context - protected GraphStoreSpatialContextImpl spatialIndex; + protected SpatialIndexImpl spatialIndex; public GraphStore() { this(null); @@ -80,17 +79,13 @@ public GraphStore(GraphModelImpl model) { configuration = model != null ? model.configuration : new Configuration(); graphModel = model; lock = new GraphLock(); - if (GraphStoreConfiguration.ENABLE_SPATIAL_INDEX) { - spatialIndex = new GraphStoreSpatialContextImpl(this); - } else { - spatialIndex = null; - } edgeTypeStore = new EdgeTypeStore(); mainGraphView = new MainGraphView(); viewStore = new GraphViewStore(this); version = GraphStoreConfiguration.ENABLE_OBSERVERS ? new GraphVersion(this) : null; observers = GraphStoreConfiguration.ENABLE_OBSERVERS ? new ArrayList<>() : null; + spatialIndex = GraphStoreConfiguration.ENABLE_SPATIAL_INDEX ? new SpatialIndexImpl(this) : null; edgeStore = new EdgeStore(edgeTypeStore, spatialIndex, GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? lock : null, viewStore, GraphStoreConfiguration.ENABLE_OBSERVERS ? version : null); nodeStore = new NodeStore(edgeStore, spatialIndex, GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? lock : null, @@ -782,19 +777,21 @@ protected void destroyGraphObserver(GraphObserverImpl observer) { } protected EdgeIterableWrapper getEdgeIterableWrapper(Iterator edgeIterator) { - return new EdgeIterableWrapper(edgeIterator); + return getEdgeIterableWrapper(edgeIterator, true); } protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator) { - return new NodeIterableWrapper(nodeIterator); + return getNodeIterableWrapper(nodeIterator, true); } protected EdgeIterableWrapper getEdgeIterableWrapper(Iterator edgeIterator, boolean blocking) { - return new EdgeIterableWrapper(edgeIterator, blocking); + return new EdgeIterableWrapper(edgeIterator, (blocking && GraphStoreConfiguration.ENABLE_AUTO_LOCKING) ? lock + : null); } protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator, boolean blocking) { - return new NodeIterableWrapper(nodeIterator, blocking); + return new NodeIterableWrapper(nodeIterator, (blocking && GraphStoreConfiguration.ENABLE_AUTO_LOCKING) ? lock + : null); } public int deepHashCode() { @@ -830,101 +827,6 @@ public boolean deepEquals(GraphStore obj) { return true; } - @Override - public SpatialContext getSpatialContext() { - return spatialIndex; - } - - protected class NodeIterableWrapper implements NodeIterable { - - protected final Iterator iterator; - protected final boolean blocking; - - public NodeIterableWrapper(Iterator iterator) { - this(iterator, true); - } - - public NodeIterableWrapper(Iterator iterator, boolean blocking) { - this.iterator = iterator; - this.blocking = blocking; - } - - @Override - public Iterator iterator() { - return iterator; - } - - @Override - public Node[] toArray() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list.toArray(new Node[0]); - } - - @Override - public Collection toCollection() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list; - } - - @Override - public void doBreak() { - if (blocking) { - autoReadUnlock(); - } - } - } - - protected class EdgeIterableWrapper implements EdgeIterable { - - protected final Iterator iterator; - protected final boolean blocking; - - public EdgeIterableWrapper(Iterator iterator) { - this(iterator, true); - } - - public EdgeIterableWrapper(Iterator iterator, boolean blocking) { - this.iterator = iterator; - this.blocking = blocking; - } - - @Override - public Iterator iterator() { - return iterator; - } - - @Override - public Edge[] toArray() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list.toArray(new Edge[0]); - } - - @Override - public Collection toCollection() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list; - } - - @Override - public void doBreak() { - if (blocking) { - autoReadUnlock(); - } - } - } - private final class MainGraphView implements GraphView { @Override diff --git a/store/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/store/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java index 438d6d0e..6172d8c5 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java @@ -77,6 +77,8 @@ public final class GraphStoreConfiguration { public static final Estimator DEFAULT_ESTIMATOR = Estimator.FIRST; public static final TimeRepresentation DEFAULT_TIME_REPRESENTATION = TimeRepresentation.TIMESTAMP; // Spatial index + public static final int SPATIAL_INDEX_MAX_LEVELS = 16; + public static final int SPATIAL_INDEX_MAX_OBJECTS_PER_NODE = 5000; public static final float SPATIAL_INDEX_DIMENSION_BOUNDARY = 1e6f; // Miscellaneous public static final double TIMESTAMP_STORE_GROWING_FACTOR = 1.1; diff --git a/store/src/main/java/org/gephi/graph/impl/GraphStoreSpatialContextImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphStoreSpatialContextImpl.java deleted file mode 100644 index 10ce47dd..00000000 --- a/store/src/main/java/org/gephi/graph/impl/GraphStoreSpatialContextImpl.java +++ /dev/null @@ -1,235 +0,0 @@ -package org.gephi.graph.impl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Consumer; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.EdgeIterable; -import org.gephi.graph.api.Node; -import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.Rect2D; -import org.gephi.graph.api.SpatialContext; - -/** - * Graph spatial indexing interface. - * TODO: unit tests!! - * TODO: measure performance loss due to having this. - * @author Eduardo Ramos - */ -public class GraphStoreSpatialContextImpl implements SpatialContext { - - private final GraphStore store; - private final NodesQuadTree nodesTree; - private final EdgesQuadTree edgesTree; - - public GraphStoreSpatialContextImpl(GraphStore store) { - this.store = store; - this.nodesTree = new NodesQuadTree(GraphStoreConfiguration.SPATIAL_INDEX_DIMENSION_BOUNDARY); - this.edgesTree = new EdgesQuadTree(GraphStoreConfiguration.SPATIAL_INDEX_DIMENSION_BOUNDARY); - } - - @Override - public NodeIterable getNodesInArea(Rect2D rect) { - return nodesTree.getNodes(rect); - } - - @Override - public void getNodesInArea(Rect2D rect, Consumer callback) { - nodesTree.getNodes(rect, callback); - } - - @Override - public EdgeIterable getEdgesInArea(Rect2D rect) { - return edgesTree.getEdges(rect); - } - - @Override - public void getEdgesInArea(Rect2D rect, Consumer callback) { - edgesTree.getEdges(rect, callback); - } - - protected void clearNodes() { - nodesTree.clear(); - } - - protected void addNode(final Node node) { - final float x = node.x(); - final float y = node.y(); - final float size = node.size(); - - final float minX = x - size; - final float minY = y - size; - final float maxX = x + size; - final float maxY = y + size; - - nodesTree.addNode(node, minX, minY, maxX, maxY); - } - - protected void removeNode(final Node node) { - nodesTree.removeNode(node); - } - - protected void moveNode(final Node node) { - final float x = node.x(); - final float y = node.y(); - final float size = node.size(); - - final float minX = x - size; - final float minY = y - size; - final float maxX = x + size; - final float maxY = y + size; - - nodesTree.updateNode(node, minX, minY, maxX, maxY); - - // Update node edges: - for (Edge edge : store.getEdges(node)) { - final Node opposite = edge.getSource() == node ? edge.getTarget() : edge.getSource(); - - final float x2 = opposite.x(); - final float y2 = opposite.y(); - - edgesTree.updateEdge(edge, min(x, x2), min(y, y2), max(x, x2), max(y, y2)); - } - } - - protected void addEdge(Edge edge) { - final Node source = edge.getSource(); - final Node target = edge.getTarget(); - - final float x1 = source.x(); - final float y1 = source.y(); - final float x2 = target.x(); - final float y2 = target.y(); - - final float minX = min(x1, x2); - final float minY = min(y1, y2); - final float maxX = max(x1, x2); - final float maxY = max(y1, y2); - - edgesTree.addEdge(edge, minX, minY, maxX, maxY); - } - - protected void removeEdge(Edge edge) { - edgesTree.removeEdge(edge); - } - - protected void clearEdges() { - edgesTree.clear(); - } - - private static float min(float a, float b) { - return (a <= b) ? a : b; - } - - private static float max(float a, float b) { - return (a >= b) ? a : b; - } - - protected EdgeIterableWrapper getEdgeIterableWrapper(Iterator edgeIterator) { - return new EdgeIterableWrapper(edgeIterator); - } - - protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator) { - return new NodeIterableWrapper(nodeIterator); - } - - protected EdgeIterableWrapper getEdgeIterableWrapper(Iterator edgeIterator, boolean blocking) { - return new EdgeIterableWrapper(edgeIterator, blocking); - } - - protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator, boolean blocking) { - return new NodeIterableWrapper(nodeIterator, blocking); - } - - protected class NodeIterableWrapper implements NodeIterable { - - protected final Iterator iterator; - protected final boolean blocking; - - public NodeIterableWrapper(Iterator iterator) { - this(iterator, true); - } - - public NodeIterableWrapper(Iterator iterator, boolean blocking) { - this.iterator = iterator; - this.blocking = blocking; - } - - @Override - public Iterator iterator() { - return iterator; - } - - @Override - public Node[] toArray() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list.toArray(new Node[0]); - } - - @Override - public Collection toCollection() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list; - } - - @Override - public void doBreak() { - if (blocking) { - nodesTree.readUnlock(); - } - } - } - - protected class EdgeIterableWrapper implements EdgeIterable { - - protected final Iterator iterator; - protected final boolean blocking; - - public EdgeIterableWrapper(Iterator iterator) { - this(iterator, true); - } - - public EdgeIterableWrapper(Iterator iterator, boolean blocking) { - this.iterator = iterator; - this.blocking = blocking; - } - - @Override - public Iterator iterator() { - return iterator; - } - - @Override - public Edge[] toArray() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list.toArray(new Edge[0]); - } - - @Override - public Collection toCollection() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list; - } - - @Override - public void doBreak() { - if (blocking) { - edgesTree.readUnlock(); - } - } - } -} diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 3c7db5a3..ed1391a1 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -29,11 +29,11 @@ import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; import org.gephi.graph.api.Rect2D; -import org.gephi.graph.api.SpatialContext; +import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedSubgraph; -public class GraphViewDecorator implements DirectedSubgraph, UndirectedSubgraph, SpatialContext { +public class GraphViewDecorator implements DirectedSubgraph, UndirectedSubgraph, SpatialIndex { protected final boolean undirected; protected final GraphViewImpl view; @@ -756,15 +756,10 @@ boolean isUndirectedToIgnore(final EdgeImpl edge) { return false; } - @Override - public SpatialContext getSpatialContext() { - return this; - } - @Override public NodeIterable getNodesInArea(Rect2D rect) { Iterator iterator = graphStore.spatialIndex.getNodesInArea(rect).iterator(); - return graphStore.spatialIndex.getNodeIterableWrapper(new NodeViewIterator(iterator)); + return new NodeIterableWrapper(new NodeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); } @Override @@ -782,7 +777,7 @@ public void accept(Node node) { @Override public EdgeIterable getEdgesInArea(Rect2D rect) { Iterator iterator = graphStore.spatialIndex.getEdgesInArea(rect).iterator(); - return graphStore.spatialIndex.getEdgeIterableWrapper(new EdgeViewIterator(iterator)); + return new EdgeIterableWrapper(new EdgeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); } @Override diff --git a/store/src/main/java/org/gephi/graph/impl/NodeImpl.java b/store/src/main/java/org/gephi/graph/impl/NodeImpl.java index b3e0029f..0b2c2edb 100644 --- a/store/src/main/java/org/gephi/graph/impl/NodeImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/NodeImpl.java @@ -164,6 +164,14 @@ public TextPropertiesImpl getTextProperties() { return properties.getTextProperties(); } + protected SpatialNodeDataImpl getSpatialData() { + return properties.getSpatialData(); + } + + protected void setSpatialDate(SpatialNodeDataImpl spatialData) { + properties.setSpatialData(spatialData); + } + private void updateNodeInSpatialIndex() { if (storeId != NodeStore.NULL_ID && graphStore != null && graphStore.spatialIndex != null) { graphStore.spatialIndex.moveNode(this); @@ -273,6 +281,7 @@ protected static class NodePropertiesImpl implements NodeProperties { protected float size; protected boolean fixed; protected LayoutData layoutData; + protected SpatialNodeDataImpl spatialData; public NodePropertiesImpl() { this.textProperties = new TextPropertiesImpl(); @@ -419,6 +428,14 @@ public void setLayoutData(LayoutData layoutData) { this.layoutData = layoutData; } + public SpatialNodeDataImpl getSpatialData() { + return spatialData; + } + + public void setSpatialData(SpatialNodeDataImpl spatialData) { + this.spatialData = spatialData; + } + public int deepHashCode() { int hash = 3; hash = 53 * hash + Float.floatToIntBits(this.x); diff --git a/store/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java b/store/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java new file mode 100644 index 00000000..429c9ae3 --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java @@ -0,0 +1,21 @@ +package org.gephi.graph.impl; + +import java.util.Iterator; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeIterable; + +public class NodeIterableWrapper extends ElementIterableWrapper implements NodeIterable { + + public NodeIterableWrapper(Iterator iterator) { + super(iterator); + } + + public NodeIterableWrapper(Iterator iterator, GraphLock lock) { + super(iterator, lock); + } + + @Override + public Node[] toArray() { + return toArray(new Node[0]); + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/NodeStore.java b/store/src/main/java/org/gephi/graph/impl/NodeStore.java index 8aae6768..53a643f7 100644 --- a/store/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/store/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -31,7 +31,7 @@ public class NodeStore implements Collection, NodeIterable { protected final static int NULL_ID = -1; // Store protected final EdgeStore edgeStore; - protected final GraphStoreSpatialContextImpl spatialIndex; + protected final SpatialIndexImpl spatialIndex; // Locking (optional) protected final GraphLock lock; // Version @@ -56,7 +56,7 @@ public NodeStore() { this.spatialIndex = null; } - public NodeStore(final EdgeStore edgeStore, final GraphStoreSpatialContextImpl spatialIndex, final GraphLock lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { + public NodeStore(final EdgeStore edgeStore, final SpatialIndexImpl spatialIndex, final GraphLock lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { initStore(); this.lock = lock; this.edgeStore = edgeStore; @@ -261,8 +261,8 @@ public boolean add(final Node n) { } node.indexAttributes(); - if (this.spatialIndex != null) { - this.spatialIndex.addNode(n); + if (spatialIndex != null) { + spatialIndex.addNode(node); } size++; @@ -288,8 +288,8 @@ public boolean remove(final Object o) { viewStore.removeNode(node); } - if (this.spatialIndex != null) { - this.spatialIndex.removeNode(node); + if (spatialIndex != null) { + spatialIndex.removeNode(node); } node.clearAttributes(); diff --git a/store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index cb96bba5..34a15a91 100644 --- a/store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -5,10 +5,8 @@ import java.util.Collection; import java.util.Deque; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.Consumer; import org.gephi.graph.api.Node; @@ -19,28 +17,26 @@ * Adapted from https://bitbucket.org/C3/quadtree/wiki/Home * * TODO: unit tests!! + * * @author Eduardo Ramos */ public class NodesQuadTree { - private static final int MAX_LEVELS = 16; - private static final int MAX_OBJECTS_PER_NODE = 2; + protected final GraphLock lock = new GraphLock(); - private final GraphLock lock = new GraphLock(); - private Map wrappedDictionary = new LinkedHashMap<>(); - - private QuadTreeNode quadTreeRoot; + private final QuadTreeNode quadTreeRoot; + private final int maxLevels; + private final int maxObjectsPerNode; public NodesQuadTree(Rect2D rect) { - quadTreeRoot = new QuadTreeNode(rect); - } - - public NodesQuadTree(float dimensionMax) { - this(-dimensionMax, -dimensionMax, dimensionMax, dimensionMax); + this(rect, GraphStoreConfiguration.SPATIAL_INDEX_MAX_LEVELS, + GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE); } - public NodesQuadTree(float minX, float minY, float maxX, float maxY) { - quadTreeRoot = new QuadTreeNode(new Rect2D(minX, minY, maxX, maxY)); + public NodesQuadTree(Rect2D rect, int maxLevels, int maxObjectsPerNode) { + this.quadTreeRoot = new QuadTreeNode(rect); + this.maxLevels = maxLevels; + this.maxObjectsPerNode = maxObjectsPerNode; } public Rect2D quadRect() { @@ -71,13 +67,13 @@ public void getAllNodes(Consumer callback) { quadTreeRoot.getAllNodes(callback); } - public boolean updateNode(Node item, float minX, float minY, float maxX, float maxY) { + public boolean updateNode(NodeImpl item, float minX, float minY, float maxX, float maxY) { writeLock(); try { - final QuadTreeObject obj = wrappedDictionary.get(item); + final SpatialNodeDataImpl obj = item.getSpatialData(); if (obj != null) { - obj.updateItemCoords(minX, minY, maxX, maxY); - quadTreeRoot.update(obj); + obj.updateBoundaries(minX, minY, maxX, maxY); + quadTreeRoot.update(item); return true; } else { return false; @@ -87,26 +83,23 @@ public boolean updateNode(Node item, float minX, float minY, float maxX, float m } } - public boolean addNode(Node item) { - final float x = item.x(); - final float y = item.y(); - final float size = item.size(); - - final float minX = x - size; - final float minY = y - size; - final float maxX = x + size; - final float maxY = y + size; - - return addNode(item, minX, minY, maxX, maxY); - } - - public boolean addNode(Node item, float minX, float minY, float maxX, float maxY) { + public boolean addNode(NodeImpl item) { writeLock(); try { - if (!containsNode(item)) { - final QuadTreeObject wrappedObject = new QuadTreeObject(item, minX, minY, maxX, maxY); - wrappedDictionary.put(item, wrappedObject); - quadTreeRoot.insert(wrappedObject); + final float x = item.x(); + final float y = item.y(); + final float size = item.size(); + + final float minX = x - size; + final float minY = y - size; + final float maxX = x + size; + final float maxY = y + size; + + SpatialNodeDataImpl spatialData = item.getSpatialData(); + if (spatialData == null) { + spatialData = new SpatialNodeDataImpl(minX, minY, maxX, maxY); + item.setSpatialDate(spatialData); + quadTreeRoot.insert(item); return true; } else { return false; @@ -119,47 +112,37 @@ public boolean addNode(Node item, float minX, float minY, float maxX, float maxY public void clear() { writeLock(); try { - wrappedDictionary.clear(); + for (Node node : getAllNodes()) { + SpatialNodeDataImpl spatialData = ((NodeImpl) node).getSpatialData(); + spatialData.setQuadTreeNode(null); + } quadTreeRoot.clear(); } finally { writeUnlock(); } } - public boolean containsNode(Node item) { - readLock(); - try { - return wrappedDictionary.containsKey(item); - } finally { - readUnlock(); - } - } - - public int count() { - readLock(); - try { - return wrappedDictionary.size(); - } finally { - readUnlock(); - } - } - - public boolean removeNode(Node item) { + public boolean removeNode(NodeImpl item) { writeLock(); try { - final QuadTreeObject obj = wrappedDictionary.get(item); - if (obj != null) { - quadTreeRoot.delete(obj, true); - wrappedDictionary.remove(item); + final SpatialNodeDataImpl spatialData = item.getSpatialData(); + if (spatialData != null && spatialData.quadTreeNode != null) { + quadTreeRoot.delete(item, true); return true; - } else { - return false; } + return false; } finally { writeUnlock(); } } + public int getObjectCount() { + readLock(); + int count = quadTreeRoot.objectCount(); + readUnlock(); + return count; + } + public void readLock() { if (lock != null) { lock.readLock(); @@ -193,29 +176,16 @@ public String toString() { return sb.toString(); } - private class QuadTreeObject { - - private final Node data; - private float minX, minY, maxX, maxY; - - private QuadTreeNode owner; - - public QuadTreeObject(Node data, float minX, float minY, float maxX, float maxY) { - this.data = data; - updateItemCoords(minX, minY, maxX, maxY); - } - - private void updateItemCoords(float minX, float minY, float maxX, float maxY) { - this.minX = minX; - this.minY = minY; - this.maxX = maxX; - this.maxY = maxY; - } + public int getDepth() { + readLock(); + int depth = quadTreeRoot.getDepth(); + readUnlock(); + return depth; } - private class QuadTreeNode { + protected class QuadTreeNode { - private Set objects = null; + private Set objects = null; private final Rect2D rect; // The area this QuadTree represents private final QuadTreeNode parent; // The parent of this quad @@ -268,16 +238,16 @@ private QuadTreeNode(QuadTreeNode parent, int level, Rect2D rect) { this.parent = parent; } - private void add(QuadTreeObject item) { + private void add(NodeImpl item) { if (objects == null) { objects = new LinkedHashSet<>(); } - item.owner = this; + item.getSpatialData().setQuadTreeNode(this); objects.add(item); } - private void remove(QuadTreeObject item) { + private void remove(NodeImpl item) { if (objects != null) { objects.remove(item); } @@ -318,9 +288,9 @@ private void subdivide() { childBR = new QuadTreeNode(this, level + 1, new Rect2D(halfX, halfY, maxX, maxY)); // If they're completely contained by the quad, bump objects down - final Iterator iterator = objects.iterator(); + final Iterator iterator = objects.iterator(); while (iterator.hasNext()) { - QuadTreeObject obj = iterator.next(); + NodeImpl obj = iterator.next(); QuadTreeNode destTree = getDestinationTree(obj); if (destTree != this) { // Insert to the appropriate tree, remove the object, and @@ -332,14 +302,15 @@ private void subdivide() { } } - private QuadTreeNode getDestinationTree(QuadTreeObject item) { + private QuadTreeNode getDestinationTree(NodeImpl item) { // If a child can't contain an object, it will live in this Quad final QuadTreeNode destTree; - final float minX = item.minX; - final float minY = item.minY; - final float maxX = item.maxX; - final float maxY = item.maxY; + SpatialNodeDataImpl spatialData = item.getSpatialData(); + final float minX = spatialData.minX; + final float minY = spatialData.minY; + final float maxX = spatialData.maxX; + final float maxY = spatialData.maxY; if (childTL.quadRect().contains(minX, minY, maxX, maxY)) { destTree = childTL; @@ -356,18 +327,20 @@ private QuadTreeNode getDestinationTree(QuadTreeObject item) { return destTree; } - private void relocate(QuadTreeObject item) { + private void relocate(NodeImpl item) { + SpatialNodeDataImpl spatialData = item.getSpatialData(); + // Are we still inside our parent? - if (quadRect().contains(item.minX, item.minY, item.maxX, item.maxY)) { + if (quadRect().contains(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY)) { // Good, have we moved inside any of our children? if (childTL != null) { QuadTreeNode dest = getDestinationTree(item); - if (item.owner != dest) { + if (spatialData.quadTreeNode != dest) { // Delete the item from this quad and add it to our // child // Note: Do NOT clean during this call, it can // potentially delete our destination quad - QuadTreeNode formerOwner = item.owner; + QuadTreeNode formerOwner = spatialData.quadTreeNode; delete(item, false); dest.insert(item); @@ -426,23 +399,25 @@ private void clear() { childBR = null; } - private void delete(QuadTreeObject item, boolean clean) { - if (item.owner != null) { - if (item.owner == this) { - remove(item); + private void delete(NodeImpl node, boolean clean) { + SpatialNodeDataImpl spatialData = node.getSpatialData(); + if (spatialData.quadTreeNode != null) { + if (spatialData.quadTreeNode == this) { + remove(node); if (clean) { cleanUpwards(); } } else { - item.owner.delete(item, clean); + spatialData.quadTreeNode.delete(node, clean); } } } - private void insert(QuadTreeObject item) { + private void insert(NodeImpl item) { + SpatialNodeDataImpl spatialData = item.getSpatialData(); // If this quad doesn't contain the items rectangle, do nothing, // unless we are the root - if (!rect.contains(item.minX, item.minY, item.maxX, item.maxY)) { + if (!rect.contains(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY)) { if (parent == null) { // This object is outside of the QuadTreeXNA bounds, we // should add it at the root level @@ -453,7 +428,7 @@ private void insert(QuadTreeObject item) { } } - if (objects == null || (childTL == null && (level >= MAX_LEVELS || objects.size() + 1 <= MAX_OBJECTS_PER_NODE))) { + if (objects == null || (childTL == null && (level >= maxLevels || objects.size() + 1 <= maxObjectsPerNode))) { // If there's room to add the object, just add it add(item); } else { @@ -485,9 +460,11 @@ private void getNodes(Rect2D searchRect, Consumer callback) { this.getAllNodes(callback); } else if (searchRect.intersects(this.rect)) { if (objects != null && !objects.isEmpty()) { - for (QuadTreeObject obj : objects) { - if (searchRect.intersects(obj.minX, obj.minY, obj.maxX, obj.maxY)) { - callback.accept(obj.data); + for (NodeImpl obj : objects) { + SpatialNodeDataImpl spatialData = obj.getSpatialData(); + if (searchRect + .intersects(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY)) { + callback.accept(obj); } } } @@ -503,8 +480,8 @@ private void getNodes(Rect2D searchRect, Consumer callback) { private void getAllNodes(Consumer callback) { if (objects != null && !objects.isEmpty()) { - for (QuadTreeObject obj : objects) { - callback.accept(obj.data); + for (NodeImpl obj : objects) { + callback.accept(obj); } } @@ -516,14 +493,26 @@ private void getAllNodes(Consumer callback) { } } - private void update(QuadTreeObject item) { - if (item.owner != null) { - item.owner.relocate(item); + private void update(NodeImpl item) { + SpatialNodeDataImpl spatialData = item.getSpatialData(); + if (spatialData.quadTreeNode != null) { + spatialData.quadTreeNode.relocate(item); } else { relocate(item); } } + private int getDepth() { + int maxLevel = level; + if (childTL != null) { + maxLevel = Math.max(maxLevel, childTL.getDepth()); + maxLevel = Math.max(maxLevel, childBR.getDepth()); + maxLevel = Math.max(maxLevel, childTR.getDepth()); + maxLevel = Math.max(maxLevel, childBL.getDepth()); + } + return maxLevel; + } + public void toString(StringBuilder sb) { for (int i = 0; i < level; i++) { sb.append(" "); @@ -531,12 +520,12 @@ public void toString(StringBuilder sb) { sb.append(rect.toString()).append('\n'); if (objects != null) { - for (QuadTreeObject object : objects) { + for (NodeImpl object : objects) { for (int i = 0; i <= level; i++) { sb.append(" "); } - sb.append(object.data.getId()).append('\n'); + sb.append(object.getId()).append('\n'); } } @@ -565,16 +554,15 @@ public Iterator iterator() { @Override public Node[] toArray() { final Collection collection = toCollection(); - return collection.toArray(new Node[collection.size()]); + return collection.toArray(new Node[0]); } @Override public Collection toCollection() { final List list = new ArrayList<>(); - final Iterator iterator = iterator(); - while (iterator.hasNext()) { - list.add(iterator.next()); + for (Node node : this) { + list.add(node); } return list; @@ -594,11 +582,11 @@ private class QuadTreeNodesIterator implements Iterator { private final Deque fullyContainedStack = new ArrayDeque<>(); // Current: - private Iterator currentIterator; - private boolean currentFullyContained = false; + private Iterator currentIterator; + private boolean currentFullyContained; private boolean finished = false; - private QuadTreeObject next; + private NodeImpl next; public QuadTreeNodesIterator(QuadTreeNode root, Rect2D searchRect) { this.searchRect = searchRect; @@ -642,9 +630,11 @@ public boolean hasNext() { while (currentIterator != null || !nodesStack.isEmpty()) { if (currentIterator != null) { while (currentIterator.hasNext()) { - final QuadTreeObject elem = currentIterator.next(); + final NodeImpl elem = currentIterator.next(); + final SpatialNodeDataImpl spatialData = elem.getSpatialData(); - if (currentFullyContained || searchRect.intersects(elem.minX, elem.minY, elem.maxX, elem.maxY)) { + if (currentFullyContained || searchRect + .intersects(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY)) { next = elem; return true; } @@ -671,12 +661,12 @@ public boolean hasNext() { } @Override - public Node next() { + public NodeImpl next() { if (next == null) { throw new IllegalStateException("No next available!"); } - final Node node = next.data; + final NodeImpl node = next; next = null; diff --git a/store/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/store/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java new file mode 100644 index 00000000..664ffc4b --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java @@ -0,0 +1,131 @@ +package org.gephi.graph.impl; + +import java.util.Iterator; +import java.util.function.Consumer; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeIterable; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeIterable; +import org.gephi.graph.api.Rect2D; +import org.gephi.graph.api.SpatialIndex; + +/** + * Graph spatial indexing interface. + * + * @author Eduardo Ramos + */ +public class SpatialIndexImpl implements SpatialIndex { + + private final GraphStore store; + protected final NodesQuadTree nodesTree; + + public SpatialIndexImpl(GraphStore store) { + this.store = store; + float boundaries = GraphStoreConfiguration.SPATIAL_INDEX_DIMENSION_BOUNDARY; + this.nodesTree = new NodesQuadTree(new Rect2D(-boundaries, -boundaries, boundaries, boundaries)); + } + + @Override + public NodeIterable getNodesInArea(Rect2D rect) { + return nodesTree.getNodes(rect); + } + + @Override + public void getNodesInArea(Rect2D rect, Consumer callback) { + nodesTree.getNodes(rect, callback); + } + + @Override + public EdgeIterable getEdgesInArea(Rect2D rect) { + return new EdgeIterableWrapper(new EdgeIterator(rect, nodesTree.getNodes(rect).iterator()), nodesTree.lock); + } + + @Override + public void getEdgesInArea(Rect2D rect, Consumer callback) { + // TODO + } + + protected void clearNodes() { + nodesTree.clear(); + } + + protected void addNode(final NodeImpl node) { + nodesTree.addNode(node); + } + + protected void removeNode(final NodeImpl node) { + nodesTree.removeNode(node); + } + + protected void moveNode(final NodeImpl node) { + final float x = node.x(); + final float y = node.y(); + final float size = node.size(); + + final float minX = x - size; + final float minY = y - size; + final float maxX = x + size; + final float maxY = y + size; + + nodesTree.updateNode(node, minX, minY, maxX, maxY); + } + + protected class EdgeIterator implements Iterator { + + private final Iterator nodeItr; + private final Rect2D rect2D; + private Iterator edgeItr; + private Edge pointer; + private Node node; + + public EdgeIterator(Rect2D rect2D, Iterator nodeIterator) { + this.nodeItr = nodeIterator; + this.rect2D = rect2D; + + nodesTree.readLock(); + } + + @Override + public boolean hasNext() { + while (pointer == null) { + while (pointer == null && edgeItr != null && edgeItr.hasNext()) { + pointer = edgeItr.next(); + if (!pointer.isSelfLoop()) { + Node oppositeNode = store.getOpposite(node, pointer); + // Skip edge - do not return same edges twice when both + // source and target nodes are visible + SpatialNodeDataImpl spatialData = ((NodeImpl) oppositeNode).getSpatialData(); + if (oppositeNode.getStoreId() < node.getStoreId() && rect2D + .intersects(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY)) { + pointer = null; + } + } + } + if (pointer == null) { + edgeItr = null; + if (nodeItr != null && nodeItr.hasNext()) { + node = nodeItr.next(); + edgeItr = store.edgeStore.edgeIterator(node); + } else { + nodesTree.readUnlock(); + return false; + } + } + } + + return true; + } + + @Override + public Edge next() { + Edge res = pointer; + pointer = null; + return res; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java b/store/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java new file mode 100644 index 00000000..cb4a04f9 --- /dev/null +++ b/store/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java @@ -0,0 +1,26 @@ +package org.gephi.graph.impl; + +public class SpatialNodeDataImpl { + + public float minX, minY, maxX, maxY; + + protected NodesQuadTree.QuadTreeNode quadTreeNode; + + public SpatialNodeDataImpl(float minX, float minY, float maxX, float maxY) { + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + } + + public void updateBoundaries(float minX, float minY, float maxX, float maxY) { + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + } + + public void setQuadTreeNode(NodesQuadTree.QuadTreeNode quadTreeNode) { + this.quadTreeNode = quadTreeNode; + } +} diff --git a/store/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/store/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 82ee5f16..e02735c3 100644 --- a/store/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -25,7 +25,7 @@ import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.SpatialContext; +import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedGraph; import org.gephi.graph.api.UndirectedSubgraph; @@ -388,9 +388,4 @@ public void not() { public Graph getRootGraph() { return this; } - - @Override - public SpatialContext getSpatialContext() { - return store.getSpatialContext(); - } } diff --git a/store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index c32fecd5..acf4542c 100644 --- a/store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -45,7 +45,7 @@ import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.SpatialContext; +import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.Table; import org.gephi.graph.api.TextProperties; import org.gephi.graph.spi.LayoutData; @@ -1625,9 +1625,4 @@ public void doBreak() { // Not used because no locking } } - - @Override - public SpatialContext getSpatialContext() { - throw new UnsupportedOperationException("Not supported yet."); - } } diff --git a/store/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/store/src/test/java/org/gephi/graph/impl/GraphGenerator.java index 773ce21c..fbe07c7d 100644 --- a/store/src/test/java/org/gephi/graph/impl/GraphGenerator.java +++ b/store/src/test/java/org/gephi/graph/impl/GraphGenerator.java @@ -425,6 +425,16 @@ public static GraphStore generateTinyGraphStore(TimeRepresentation timeRepresent return generateTinyGraphStore(config); } + public static GraphStore generateTinyGraphStoreWithSelfLoop() { + GraphModelImpl graphModel = new GraphModelImpl(new Configuration()); + GraphStore graphStore = graphModel.store; + NodeImpl n1 = new NodeImpl("1", graphStore); + EdgeImpl e = new EdgeImpl("0", graphStore, n1, n1, EdgeTypeStore.NULL_LABEL, 1.0, true); + graphStore.addNode(n1); + graphStore.addEdge(e); + return graphStore; + } + public static GraphStore generateSmallGraphStore() { int edgeCount = 100; GraphStore graphStore = new GraphModelImpl().store; diff --git a/store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java b/store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java index 7b781f2f..0844d65d 100644 --- a/store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java +++ b/store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java @@ -2,6 +2,7 @@ import java.util.Arrays; import java.util.Collection; +import java.util.Random; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; import org.gephi.graph.api.Rect2D; @@ -13,17 +14,106 @@ public class NodesQuadTreeTest { private static final float BOUNDS = 1e6f; private static final Rect2D BOUNDS_RECT = new Rect2D(-BOUNDS, -BOUNDS, BOUNDS, BOUNDS); + @Test + public void testBoundaries() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + Assert.assertEquals(q.quadRect(), BOUNDS_RECT); + } + + @Test + public void testGetAllNodesEmpty() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + Assert.assertTrue(q.getAllNodes().toCollection().isEmpty()); + Assert.assertEquals(q.getAllNodes().toArray().length, 0); + } + + @Test + public void testGetNodesEmpty() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + Assert.assertTrue(q.getNodes(new Rect2D(-1, -1, 1, 1)).toCollection().isEmpty()); + } + + @Test + public void testRemoveEmpty() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + NodeImpl node = new NodeImpl("0"); + Assert.assertFalse(q.removeNode(node)); + } + + @Test + public void testAddNode() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + NodeImpl node = new NodeImpl("0"); + Assert.assertTrue(q.addNode(node)); + Assert.assertNotNull(node.getSpatialData().quadTreeNode); + } + + @Test + public void testAddNodeTwice() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + NodeImpl node = new NodeImpl("0"); + q.addNode(node); + Assert.assertFalse(q.addNode(node)); + } + + @Test + public void testClear() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + NodeImpl node = new NodeImpl("0"); + q.addNode(node); + q.clear(); + Assert.assertNull(node.getSpatialData().quadTreeNode); + Assert.assertTrue(q.getAllNodes().toCollection().isEmpty()); + Assert.assertFalse(q.removeNode(node)); + } + + @Test + public void testCount() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + Assert.assertEquals(q.getObjectCount(), 0); + NodeImpl node = new NodeImpl("0"); + q.addNode(node); + Assert.assertEquals(q.getObjectCount(), 1); + q.clear(); + Assert.assertEquals(q.getObjectCount(), 0); + } + + @Test + public void testUpdate() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + Assert.assertEquals(q.getObjectCount(), 0); + NodeImpl node = new NodeImpl("0"); + q.addNode(node); + Assert.assertTrue(q.updateNode(node, -100, -50, -50, 0)); + } + + @Test + public void testDepthZero() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + Assert.assertEquals(q.getDepth(), 0); + } + + @Test + public void testDepth() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + Random random = new Random(); + for (int i = 0; i <= GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE; i++) { + NodeImpl node = new NodeImpl(String.valueOf(i)); + node.setPosition(random.nextInt((int) BOUNDS * 2) - BOUNDS, random.nextInt((int) BOUNDS * 2) - BOUNDS); + q.addNode(node); + } + Assert.assertTrue(q.getDepth() >= 1); + } + @Test public void testGetAll() { final NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); - Node n1 = new NodeImpl("1"); + NodeImpl n1 = new NodeImpl("1"); n1.setPosition(100, 100); - - Node n2 = new NodeImpl("2"); + NodeImpl n2 = new NodeImpl("2"); n2.setPosition(0, 0); - - Node n3 = new NodeImpl("3"); + NodeImpl n3 = new NodeImpl("3"); n2.setPosition(-100, -100); q.addNode(n1); @@ -42,17 +132,17 @@ public void testGetAll() { @Test public void testOutOfBoundsStillWorks() { - final NodesQuadTree q = new NodesQuadTree(0, 0, 10, 10); + final NodesQuadTree q = new NodesQuadTree(new Rect2D(0, 0, 10, 10)); - Node n1 = new NodeImpl("1"); + NodeImpl n1 = new NodeImpl("1"); n1.setPosition(100, 100); n1.setSize(10); - Node n2 = new NodeImpl("2"); + NodeImpl n2 = new NodeImpl("2"); n2.setPosition(0, 0); n2.setSize(5); - Node n3 = new NodeImpl("3"); + NodeImpl n3 = new NodeImpl("3"); n3.setPosition(-100, -100); n3.setSize(3); @@ -74,15 +164,15 @@ public void testOutOfBoundsStillWorks() { public void testGetZone1() { final NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); - Node n1 = new NodeImpl("1"); + NodeImpl n1 = new NodeImpl("1"); n1.setPosition(100, 100); n1.setSize(10); - Node n2 = new NodeImpl("2"); + NodeImpl n2 = new NodeImpl("2"); n2.setPosition(0, 0); n2.setSize(5); - Node n3 = new NodeImpl("3"); + NodeImpl n3 = new NodeImpl("3"); n3.setPosition(-100, -100); n3.setSize(3); @@ -99,17 +189,17 @@ public void testGetZone1() { @Test public void testGetZone2() { - final NodesQuadTree q = new NodesQuadTree(-120, -120, 120, 120); + final NodesQuadTree q = new NodesQuadTree(new Rect2D(120, -120, 120, 120)); - Node n1 = new NodeImpl("1"); + NodeImpl n1 = new NodeImpl("1"); n1.setPosition(100, 100); n1.setSize(10); - Node n2 = new NodeImpl("2"); + NodeImpl n2 = new NodeImpl("2"); n2.setPosition(0, 0); n2.setSize(5); - Node n3 = new NodeImpl("3"); + NodeImpl n3 = new NodeImpl("3"); n3.setPosition(-100, -100); n3.setSize(3); @@ -120,23 +210,12 @@ public void testGetZone2() { assertEmpty(q.getNodes(80, 80, 89.99f, 89.99f)); assertSame(q.getNodes(95, 95, 99, 99), n1); - assertSame(q.getNodes(0, 0, 101, 101), n2, n1); - assertSame(q.getNodes(4, 4, 91, 91), n2, n1); - } - - private void assertSame(NodeIterable iterable, Collection expected) { - Collection found = iterable.toCollection(); - try { - Assert.assertEquals(found, expected); - } catch (AssertionError ex) { - System.out.println("Found: " + listIds(found)); - System.out.println("Expected: " + listIds(expected)); - throw ex; - } + assertSame(q.getNodes(0, 0, 101, 101), n1, n2); + assertSame(q.getNodes(4, 4, 91, 91), n1, n2); } private void assertSame(NodeIterable iterable, Node... expected) { - assertSame(iterable, Arrays.asList(expected)); + Assert.assertEquals(iterable.toCollection(), Arrays.asList(expected)); } private void assertEmpty(NodeIterable iterable) { diff --git a/store/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java b/store/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java new file mode 100644 index 00000000..4fb67479 --- /dev/null +++ b/store/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java @@ -0,0 +1,84 @@ +package org.gephi.graph.impl; + +import java.util.Arrays; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeIterable; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeIterable; +import org.gephi.graph.api.Rect2D; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class SpatialIndexImplTest { + + private static final float BOUNDS = 1000f; + private static final Rect2D BOUNDS_RECT = new Rect2D(-BOUNDS, -BOUNDS, BOUNDS, BOUNDS); + + @Test + public void testGetEdgesEmpty() { + SpatialIndexImpl spatialIndex = new GraphStore().spatialIndex; + Assert.assertTrue(spatialIndex.getEdgesInArea(BOUNDS_RECT).toCollection().isEmpty()); + } + + @Test + public void testGetElementsBothNodesVisible() { + GraphStore store = GraphGenerator.generateTinyGraphStore(); + + NodeImpl n1 = store.getNode("1"); + NodeImpl n2 = store.getNode("2"); + EdgeImpl e = store.getEdge("0"); + + SpatialIndexImpl spatialIndex = store.spatialIndex; + assertSame(spatialIndex.getNodesInArea(BOUNDS_RECT), n1, n2); + assertSame(spatialIndex.getEdgesInArea(BOUNDS_RECT), e); + } + + @Test + public void testGetElementsOneNodeVisible() { + GraphStore store = GraphGenerator.generateTinyGraphStore(); + + NodeImpl n1 = store.getNode("1"); + n1.setPosition(300000f, 300000f); + NodeImpl n2 = store.getNode("2"); + EdgeImpl e = store.getEdge("0"); + + SpatialIndexImpl spatialIndex = store.spatialIndex; + assertSame(spatialIndex.getNodesInArea(BOUNDS_RECT), n2); + assertSame(spatialIndex.getEdgesInArea(BOUNDS_RECT), e); + } + + @Test + public void testGetElementsWithoutNodeVisible() { + GraphStore store = GraphGenerator.generateTinyGraphStore(); + + NodeImpl n1 = store.getNode("1"); + n1.setPosition(300000f, 300000f); + NodeImpl n2 = store.getNode("2"); + n2.setPosition(300001f, 300001f); + EdgeImpl e = store.getEdge("0"); + + SpatialIndexImpl spatialIndex = store.spatialIndex; + Assert.assertTrue(spatialIndex.getNodesInArea(BOUNDS_RECT).toCollection().isEmpty()); + Assert.assertTrue(spatialIndex.getEdgesInArea(BOUNDS_RECT).toCollection().isEmpty()); + } + + @Test + public void testGetElementsWithSelfLoop() { + GraphStore store = GraphGenerator.generateTinyGraphStoreWithSelfLoop(); + + NodeImpl n1 = store.getNode("1"); + EdgeImpl e = store.getEdge("0"); + + SpatialIndexImpl spatialIndex = store.spatialIndex; + assertSame(spatialIndex.getNodesInArea(BOUNDS_RECT), n1); + assertSame(spatialIndex.getEdgesInArea(BOUNDS_RECT), e); + } + + private void assertSame(NodeIterable iterable, Node... expected) { + Assert.assertEquals(iterable.toCollection(), Arrays.asList(expected)); + } + + private void assertSame(EdgeIterable iterable, Edge... expected) { + Assert.assertEquals(iterable.toCollection(), Arrays.asList(expected)); + } +} From ba4097d93164e6b19843723a16bc63957e23b30b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 13:47:50 +0100 Subject: [PATCH 022/271] Fix failing test due to list order --- store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java b/store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java index 0844d65d..d294d6a6 100644 --- a/store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java +++ b/store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java @@ -215,7 +215,7 @@ public void testGetZone2() { } private void assertSame(NodeIterable iterable, Node... expected) { - Assert.assertEquals(iterable.toCollection(), Arrays.asList(expected)); + Assert.assertEqualsNoOrder(iterable.toArray(), expected); } private void assertEmpty(NodeIterable iterable) { From faa526dc93ecd9ac60491e193ab44819339245b8 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 16:01:52 +0100 Subject: [PATCH 023/271] Wrap benchmark code as integration test --- store/pom.xml | 83 ++- .../graph/benchmark/ControlBenchmarkTest.java | 54 ++ .../benchmark/EdgeStoreBenchmarkTest.java | 84 +++ .../benchmark/NodeStoreBenchmarkTest.java | 47 ++ .../benchmarks/DataStructureBenchmark.java | 570 ++++++++++++++++++ .../benchmarks/EdgeStoreBenchmark.java | 110 ++++ .../graph/benchmark/benchmarks/Generator.java | 61 ++ .../benchmark/benchmarks/KleinbergGraph.java | 187 ++++++ .../benchmarks/LockingBenchmark.java | 270 +++++++++ .../benchmarks/NodeStoreBenchmark.java | 84 +++ .../benchmark/benchmarks/RandomGraph.java | 85 +++ .../graph/benchmark/nanobench/NanoBench.java | 369 ++++++++++++ .../graph/benchmark/util/ReporterHandler.java | 50 ++ 13 files changed, 2042 insertions(+), 12 deletions(-) create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java create mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java diff --git a/store/pom.xml b/store/pom.xml index 496f1392..5388028e 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -91,9 +91,6 @@ org.apache.maven.plugins maven-surefire-plugin 2.22.2 - - false - org.apache.maven.plugins @@ -143,10 +140,16 @@ formatter-maven-plugin 0.5.2 + + org.codehaus.mojo + build-helper-maven-plugin + 3.2.0 + + org.apache.maven.plugins maven-compiler-plugin @@ -155,29 +158,63 @@ ${maven.compiler.target} - - + + - org.apache.maven.plugins - maven-source-plugin + org.codehaus.mojo + build-helper-maven-plugin - attach-sources + add-benchmark-test-source + generate-test-sources - jar-no-fork + add-test-source + + + src/benchmark/java + + + + org.apache.maven.plugins - maven-javadoc-plugin + maven-surefire-plugin + + false + + true + + - attach-javadocs + unit-test - jar + test + test + + false + **/benchmark/** + methods + 4 + + + + + integration-test + + test + + integration-test + + false + **/benchmark/** + -Xmx2g + @@ -261,6 +298,20 @@ release + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + org.apache.maven.plugins @@ -289,6 +340,14 @@ aggregate + + + attach-javadocs + + jar + + + public GraphStore ${project.version} API Index diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java b/store/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java new file mode 100644 index 00000000..f8f998d4 --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark; + +import java.util.logging.Level; +import java.util.logging.Logger; +import org.gephi.graph.benchmark.nanobench.NanoBench; +import org.gephi.graph.benchmark.util.ReporterHandler; +import org.testng.Reporter; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.Test; + +public class ControlBenchmarkTest { + + @BeforeSuite + public void setUp() { + Logger logger = NanoBench.getLogger(); + logger.setUseParentHandlers(false); + logger.setLevel(Level.INFO); + logger.addHandler(new ReporterHandler()); + Reporter.setEscapeHtml(false); + } + + @Test + public void testControl() { + Runnable runnable = new Runnable() { + final int[] array = new int[10000000]; + int m = 0; + + @Override + public void run() { + int dummy = 0; + for (int doNotIgnoreMe : array) { + dummy += doNotIgnoreMe; + } + m += dummy; + } + }; + NanoBench.create().measure("control", runnable); + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java b/store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java new file mode 100644 index 00000000..c35570b4 --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark; + +import org.gephi.graph.benchmark.benchmarks.EdgeStoreBenchmark; +import org.gephi.graph.benchmark.nanobench.NanoBench; +import org.testng.annotations.Test; + +public class EdgeStoreBenchmarkTest { + + @Test + public void testPushStore() { + int[] n = {100, 1000, 5000}; + double[] p = {0.01, 0.1, 0.3}; + for (int nodes : n) { + for (double prob : p) { + int edges = (int) (nodes * (nodes - 1) * prob); + NanoBench + .create().measurements(2).measure("push edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().pushEdgeStore(nodes, prob)); + } + } + } + + @Test + public void testIterateStore() { + int[] n = {100, 1000, 5000}; + double[] p = {0.01, 0.1, 0.3}; + for (int nodes : n) { + for (double prob : p) { + int edges = (int) (nodes * (nodes - 1) * prob); + NanoBench.create().measurements(2).measure("iterate edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().iterateEdgeStore(nodes, prob)); + } + } + } + + @Test + public void testIterateOutNeighbors() { + int[] n = {100, 1000, 5000}; + double[] p = {0.01, 0.1, 0.3}; + for (int nodes : n) { + for (double prob : p) { + int edges = (int) (nodes * (nodes - 1) * prob); + NanoBench.create().measurements(2).measure("iterate neighbors list out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().iterateEdgeStoreNeighborsOut(nodes, prob)); + } + } + } + + @Test + public void testIterateInOutNeighbors() { + int[] n = {100, 1000, 5000}; + double[] p = {0.01, 0.1, 0.3}; + for (int nodes : n) { + for (double prob : p) { + int edges = (int) (nodes * (nodes - 1) * prob); + NanoBench.create().measurements(2).measure("iterate neighbors list in&out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().iterateEdgeStoreNeighborsInOut(nodes, prob)); + } + } + } + + @Test + public void testResetEdgeStore() { + int[] n = {100, 1000, 5000}; + double[] p = {0.01, 0.1, 0.3}; + for (int nodes : n) { + for (double prob : p) { + int edges = (int) (nodes * (nodes - 1) * prob); + NanoBench.create().measurements(2).measure("reset edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().resetEdgeStore(nodes, prob)); + } + } + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java b/store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java new file mode 100644 index 00000000..41096748 --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark; + +import org.gephi.graph.benchmark.benchmarks.NodeStoreBenchmark; +import org.gephi.graph.benchmark.nanobench.NanoBench; +import org.testng.annotations.Test; + +public class NodeStoreBenchmarkTest { + + @Test + public void testPushStore() { + int[] n = {100, 1000, 10000, 100000}; + for (int nodes : n) { + NanoBench.create().measurements(10).measure("push node store " + nodes, new NodeStoreBenchmark().pushStore(nodes)); + } + } + + @Test + public void testIterateStore() { + int[] n = {100, 1000, 10000, 100000}; + for (int nodes : n) { + NanoBench.create().cpuOnly().measurements(10).measure("iterate node store " + nodes, new NodeStoreBenchmark().iterateStore(nodes)); + } + } + + @Test + public void testResetNodeStore() { + int[] n = {100, 1000, 10000, 100000}; + for (int nodes : n) { + NanoBench.create().measurements(10).measure("reset node store "+nodes, new NodeStoreBenchmark().resetNodeStore(nodes)); + } + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java new file mode 100644 index 00000000..bb34090d --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java @@ -0,0 +1,570 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark.benchmarks; + +import cern.colt.bitvector.BitVector; +import cern.colt.map.OpenIntObjectHashMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap; +import it.unimi.dsi.fastutil.ints.IntAVLTreeSet; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntIterator; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectBigArrayBigList; +import it.unimi.dsi.fastutil.objects.ObjectBigList; +import it.unimi.dsi.fastutil.objects.ObjectList; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.gephi.graph.api.types.TimestampSet; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Random; + +/** + * + * @author mbastian + */ +public class DataStructureBenchmark { + + private static int NODES = 500000; + private static int LOW_NODES = 50000; + private Object object; + + /** + * Insertion and memory usage for Int2ObjectOpenHashMap + */ + public Runnable openHashMapMemory() { + return () -> { + int nodes = NODES; + Int2ObjectMap map = new Int2ObjectOpenHashMap(nodes); + for (int i = 0; i < nodes; i++) { + map.put(i, new Object()); + } + object = map; + }; + } + + /** + * Insertion and memory usage for Int2ObjectOpenHashMap without original + * capcity + */ + public Runnable dynamicOpenHashMapMemory() { + return () -> { + int nodes = NODES; + Int2ObjectMap map = new Int2ObjectOpenHashMap(); + for (int i = 0; i < nodes; i++) { + map.put(i, new Object()); + } + object = map; + }; + } + + /** + * Insertion and memory usage for Int2ObjectRBTreeMap + */ + public Runnable rbHashMapMemory() { + return () -> { + int nodes = NODES; + Int2ObjectMap map = new Int2ObjectRBTreeMap(); + for (int i = 0; i < nodes; i++) { + map.put(i, new Object()); + } + object = map; + }; + } + + /** + * Insertion and memory usage for OpenIntObjectHashMap + */ + public Runnable coltOpenHashMapMemory() { + return () -> { + int nodes = NODES; + OpenIntObjectHashMap map = new OpenIntObjectHashMap(nodes); + for (int i = 0; i < nodes; i++) { + map.put(i, new Object()); + } + object = map; + }; + } + + /** + * Insertion and memory usage for basic array + */ + public Runnable arrayMemory() { + return () -> { + int nodes = NODES; + Object[] array = new Object[nodes]; + for (int i = 0; i < nodes; i++) { + array[i] = new Object(); + } + object = array; + }; + } + + /** + * Insertion and memory usage for object list + */ + public Runnable objectListMemory() { + return () -> { + int nodes = NODES; + final ObjectList list = new ObjectArrayList(nodes); + for (int i = 0; i < nodes; i++) { + list.add(1); + } + object = list; + }; + } + + public Runnable dynamicObjectListMemory() { + return () -> { + final ObjectList list = new ObjectArrayList(); + int nodes = NODES; + for (int i = 0; i < nodes; i++) { + list.add(1); + } + object = list; + }; + } + + /** + * Insertion and memory usage for object list + */ + public Runnable objectBigListMemory() { + return () -> { + int nodes = NODES; + final ObjectBigList list = new ObjectBigArrayBigList(nodes); + for (int i = 0; i < nodes; i++) { + list.add(1); + } + object = list; + }; + } + + public Runnable openHashMapIteration() { + int nodes = NODES; + final Int2ObjectMap map = new Int2ObjectOpenHashMap(nodes); + for (int i = 0; i < nodes; i++) { + map.put(i, new Integer(1)); + } + return () -> { + int sum = 0; + for (Object i : map.values()) { + sum += (Integer) i; + } + object = sum; + }; + } + + public Runnable rbHashMapIteration() { + int nodes = NODES; + final Int2ObjectMap map = new Int2ObjectRBTreeMap(); + for (int i = 0; i < nodes; i++) { + map.put(i, new Integer(1)); + } + return () -> { + int sum = 0; + for (Object i : map.values()) { + sum += (Integer) i; + } + object = sum; + }; + } + + public Runnable arrayIteration() { + //Create array + int nodes = NODES; + final Object[] array = new Object[nodes]; + for (int i = 0; i < nodes; i++) { + array[i] = 1; + } + return () -> { + int sum = 0; + for (Object i : array) { + sum += (Integer) i; + } + object = sum; + }; + } + + public Runnable linkedListIteration() { + //Create array + int nodes = NODES; + final LinkedList list = new LinkedList(); + for (int i = 0; i < nodes; i++) { + list.add(1); + } + return () -> { + int sum = 0; + for (Object i : list) { + sum += (Integer) i; + } + object = sum; + }; + } + + public Runnable objectListIteration() { + int nodes = NODES; + final ObjectList list = new ObjectArrayList(nodes); + for (int i = 0; i < nodes; i++) { + list.add(1); + } + return () -> { + int sum = 0; + for (Object i : list) { + sum += (Integer) i; + } + object = sum; + }; + } + + public Runnable objectBigListIteration() { + int nodes = NODES; + final ObjectBigList list = new ObjectBigArrayBigList(nodes); + for (int i = 0; i < nodes; i++) { + list.add(1); + } + return () -> { + int sum = 0; + for (Object i : list) { + sum += (Integer) i; + } + object = sum; + }; + } + + public Runnable objectLinkedHashMapIteration() { + int nodes = NODES; + final Int2ObjectLinkedOpenHashMap list = new Int2ObjectLinkedOpenHashMap(nodes); + for (int i = 0; i < nodes; i++) { + list.put(new Integer(i), new Integer(1)); + } + return () -> { + int sum = 0; + for (Object i : list.values()) { + sum += (Integer) i; + } + object = sum; + }; + } + + public Runnable objectHashObjectSet() { + int nodes = NODES; + final ObjectOpenHashSet list = new ObjectOpenHashSet(nodes); + for (int i = 0; i < nodes; i++) { + list.add(i); + } + return () -> { + int sum = 0; + for (Object i : list) { + sum += (Integer) i; + } + object = sum; + }; + } + + public Runnable objectAVLObjectSet() { + int nodes = NODES; + final ObjectAVLTreeSet list = new ObjectAVLTreeSet(); + for (int i = 0; i < nodes; i++) { + list.add(i); + } + return () -> { + int sum = 0; + for (Object i : list) { + sum += (Integer) i; + } + object = sum; + }; + } + + public Runnable openHashMapResetAll() { + int nodes = LOW_NODES; + final Int2IntOpenHashMap map = new Int2IntOpenHashMap(nodes); + for (int i = 0; i < nodes; i++) { + map.put(i, i); + } + return () -> { + int nodes1 = LOW_NODES; + for (int i = 0; i < nodes1; i++) { + map.put(i, i); + } + }; + } + + public Runnable arrayResetAll() { + int nodes = LOW_NODES; + final int[] array = new int[nodes]; + for (int i = 0; i < nodes; i++) { + array[i] = i; + } + return () -> { + int nodes1 = LOW_NODES; + for (int i = 0; i < nodes1; i++) { + array[i] = i; + } + }; + } + + public Runnable objectListResetAll() { + int nodes = LOW_NODES; + final IntList list = new IntArrayList(nodes); + for (int i = 0; i < nodes; i++) { + list.add(i); + } + return () -> { + int nodes1 = LOW_NODES; + for (int i = 0; i < nodes1; i++) { + list.set(i, i); + } + }; + } + + public Runnable fastutilObject2IntIterate() { + int nodes = NODES; + final Object2IntMap map = new Object2IntOpenHashMap<>(); + for (int i = 0; i < nodes; i++) { + map.put("" + i, i); + } + final Random rand = new Random(456); + return () -> { + int matches = 0; + for (int i = 0; i < 50000; i++) { + int id = rand.nextInt(NODES - 1); + if (map.containsKey("" + id)) { + matches += id; + } + } + object = matches; + }; + } + + public Runnable javaObject2IntIterate() { + int nodes = NODES; + final Map map = new HashMap<>(); + for (int i = 0; i < nodes; i++) { + map.put("" + i, i); + } + final Random rand = new Random(456); + return () -> { + int matches = 0; + for (int i = 0; i < 50000; i++) { + int id = rand.nextInt(NODES - 1); + if (map.containsKey("" + id)) { + matches += id; + } + } + object = matches; + }; + } + + public Runnable javaArrayIterate() { + final boolean[] bitset = new boolean[NODES]; + Random rand = new Random(23); + for (int i = 0; i < NODES; i++) { + bitset[i] = rand.nextBoolean(); + } + return () -> { + int cardinality = 0; + for (int i = 0; i < NODES; i++) { + boolean b = bitset[i]; + if (b) { + cardinality++; + } + } + object = cardinality; + }; + } + + public Runnable javaBitVectorIterate() { + final BitSet bitset = new BitSet(NODES); + Random rand = new Random(23); + for (int i = 0; i < NODES; i++) { + bitset.set(i, rand.nextBoolean()); + } + return () -> { + int cardinality = 0; + for (int i = 0; i < NODES; i++) { + boolean b = bitset.get(i); + if (b) { + cardinality++; + } + } + object = cardinality; + }; + } + + public Runnable coltBitVector() { + final BitVector bitset = new BitVector(NODES); + Random rand = new Random(23); + for (int i = 0; i < NODES; i++) { + if (rand.nextBoolean()) { + bitset.set(i); + } + } + return () -> { + int cardinality = 0; + for (int i = 0; i < NODES; i++) { + boolean b = bitset.get(i); + if (b) { + cardinality++; + } + } + object = cardinality; + }; + } + + public Runnable coltBitVectorIterateAndMapLookup() { + final BitVector bitset = new BitVector(NODES); + final Int2IntOpenHashMap map = new Int2IntOpenHashMap(); + Random rand = new Random(23); + for (int i = 0; i < NODES; i++) { + if (rand.nextBoolean()) { + bitset.set(i); + map.put(i, i); + } + } + map.trim(); + return () -> { + int cardinality = 0; + for (int i = 0; i < NODES; i++) { + boolean b = bitset.get(i); + if (b) { + long rank = map.get(i); + cardinality += rank; + } + } + object = cardinality; + }; + } + + public Runnable javaBitVectorMemory() { + + return () -> { + BitSet bitset = new BitSet(10000000); + Random rand = new Random(23); + for (int i = 0; i < 10000000; i++) { + bitset.set(i, rand.nextBoolean()); + } + object = bitset; + }; + } + + public Runnable coltBitVectorMemory() { + + return () -> { + BitVector bitset = new BitVector(10000000); + Random rand = new Random(23); + for (int i = 0; i < 10000000; i++) { + if (rand.nextBoolean()) { + bitset.set(i); + } + } + object = bitset; + }; + } + + public Runnable sparseArrayIterate(float emptyRatio) { + final BitVector bitset = new BitVector(NODES); + final int[] values = new int[NODES]; + Random rand2 = new Random(456); + Random rand = new Random(23); + for (int i = 0; i < NODES; i++) { + if (rand.nextDouble() < emptyRatio) { + bitset.set(i); + } + values[i] = rand2.nextInt(NODES); + } + return () -> { + int cardinality = 0; + for (int i = 0; i < NODES; i++) { + boolean b = bitset.get(i); + if (b) { + int val = values[i]; + cardinality += val; + } + } + object = cardinality; + }; + } + + public Runnable intHashSetIterate(float emptyRatio) { + int nodes = (int) (NODES * (1 - emptyRatio)); + Random rand2 = new Random(456); + final IntSet values = new IntOpenHashSet(nodes); + for (int i = 0; i < nodes; i++) { + values.add(rand2.nextInt(NODES)); + } + return () -> { + int cardinality = 0; + IntIterator itr = values.iterator(); + while (itr.hasNext()) { + int i = itr.nextInt(); + cardinality += i; + } + object = cardinality; + }; + } + + public Runnable intAVLSetIterate(float emptyRatio) { + int nodes = (int) (NODES * (1 - emptyRatio)); + Random rand2 = new Random(456); + final IntSet values = new IntAVLTreeSet(); + for (int i = 0; i < nodes; i++) { + values.add(rand2.nextInt(NODES)); + } + return () -> { + int cardinality = 0; + IntIterator itr = values.iterator(); + while (itr.hasNext()) { + int i = itr.nextInt(); + cardinality += i; + } + object = cardinality; + }; + } + + public Runnable arraySetMemory() { + final int size = 10000; + final int timestamps = 100; + + return () -> { + List trees = new ArrayList<>(); + Random rand = new Random(1234); + for (int i = 0; i < size; i++) { + TimestampSet set = new TimestampSet(timestamps); + for (int j = 0; j < timestamps; j++) { + final double val = rand.nextInt(timestamps); + set.add(val); + trees.add(set); + } + } + object = trees; + }; + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java new file mode 100644 index 00000000..8e01dd4d --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java @@ -0,0 +1,110 @@ +package org.gephi.graph.benchmark.benchmarks; + +import java.util.Iterator; +import java.util.List; + +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Node; +import org.gephi.graph.impl.EdgeStore; + +/** + * + * @author mbastian, niteshbhargv + */ +public class EdgeStoreBenchmark { + + private Object object; + + public Runnable pushEdgeStore(int nodes, double prob) { + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate(); + final EdgeStore edgeStore = graph.getStore().getEdgeStore(); + final List nodeList = graph.getNodes(); + final List edgeList = graph.getEdges(); + graph.getStore().addAllNodes(nodeList); + + Runnable runnable = () -> { + edgeStore.clear(); + for(Edge edge : edgeList) { + edgeStore.add(edge); + } + }; + return runnable; + } + + public Runnable iterateEdgeStore(int nodes, double prob) { + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); + final EdgeStore edgeStore = graph.getStore().getEdgeStore(); + + Runnable runnable = () -> { + Iterator itr = edgeStore.iterator(); + for (; itr.hasNext();) { + object = itr.next(); + } + }; + return runnable; + } + + public Runnable iterateEdgeStoreNeighborsOut(int nodes, double prob) { + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); + final EdgeStore edgeStore = graph.getStore().getEdgeStore(); + final List nodeList = graph.getNodes(); + + Runnable runnable = () -> { + for (Node node : nodeList) { + Iterator itr = edgeStore.edgeOutIterator(node); + for (; itr.hasNext();) { + object = itr.next(); + } + } + }; + return runnable; + } + + public Runnable iterateEdgeStoreNeighborsInOut(int nodes, double prob) { + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); + final EdgeStore edgeStore = graph.getStore().getEdgeStore(); + final List nodeList = graph.getNodes(); + + Runnable runnable = () -> { + for (Node node : nodeList) { + Iterator itr = edgeStore.edgeIterator(node); + for (; itr.hasNext();) { + object = itr.next(); + } + } + }; + return runnable; + } + + public Runnable resetEdgeStore(int nodes, double prob) { + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); + final EdgeStore edgeStore = graph.getStore().getEdgeStore(); + final List edgeList = graph.getEdges(); + + Runnable runnable = () -> { + for (Edge e : edgeList) { + edgeStore.remove(e); + } + for (Edge e : edgeList) { + edgeStore.add(e); + } + }; + return runnable; + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java new file mode 100644 index 00000000..bd410810 --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java @@ -0,0 +1,61 @@ +package org.gephi.graph.benchmark.benchmarks; + +import java.util.ArrayList; +import java.util.List; + +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.GraphFactory; +import org.gephi.graph.api.Node; +import org.gephi.graph.impl.GraphModelImpl; +import org.gephi.graph.impl.GraphStore; + +public abstract class Generator { + + protected final GraphFactory factory; + protected final GraphStore graphStore; + protected List nodes; + protected List edges; + + public Generator() { + this(new Configuration()); + } + + public Generator(final Configuration config) { + GraphModelImpl model = new GraphModelImpl(config); + factory = model.factory(); + graphStore = model.getStore(); + nodes = new ArrayList<>(); + edges = new ArrayList<>(); + } + + public GraphStore getStore() { + return graphStore; + } + + public List getNodes() { + return nodes; + } + + public List getEdges() { + return edges; + } + + public void clean() { + nodes = null; + edges = null; + } + + public abstract Generator generate(); + + public abstract Generator commit(); + + protected void commitInner() { + for (Node node : nodes) { + graphStore.addNode(node); + } + for (Edge edge : edges) { + graphStore.addEdge(edge); + } + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java new file mode 100644 index 00000000..62d49558 --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java @@ -0,0 +1,187 @@ +/** + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark.benchmarks; + +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Node; + +import java.util.Random; + +/** + * Generates a directed connected graph. + * + * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.117.7097&rep=rep1&type=pdf + * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.83.381&rep=rep1&type=pdf + * + * n >= 2 p >= 1 p <= 2n - 2 q >= 0 q <= n^2 - p * (p + 3) / 2 - 1 for p < n q + * <= (2n - p - 3) * (2n - p) / 2 + 1 for p >= n r >= 0 + * + * Ω(n^4 * q) + * + * @author Nitesh Bhargava + */ +public class KleinbergGraph extends Generator { + + private int n = 5; + private int p = 2; + private int q = 2; + private int r = 0; + private boolean torusBased; + + /** + * User defined Kleinberg Graph no*no = number of nodes local = local + * contacts Long = long range contacts + */ + KleinbergGraph(int no, int local, int longRange) { + super(); + n = no; + p = local; + q = longRange; + r = 0; + torusBased = false; + } + + @Override + public KleinbergGraph generate() { + Random random = new Random(); + + // Creating lattice n x n + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + Node node = factory.newNode(); + nodes.add(node); + } + } + LongOpenHashSet edgeSet = new LongOpenHashSet(); + + // Creating edges from each node to p local contacts + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + for (int k = i - p; k <= i + p; ++k) { + for (int l = j - p; l <= j + p; ++l) { + if ((isTorusBased() || !isTorusBased() && k >= 0 && k < n && l >= 0 && l < n) + && d(i, j, k, l) <= p && nodes.get(i * n + j) != nodes.get(((k + n) % n) * n + ((l + n) % n))) { + Edge edge = factory.newEdge(nodes.get(i * n + j), nodes.get(((k + n) % n) * n + ((l + n) % n)), 0, true); + Object id = edge.getId(); + if (id instanceof Number) { + edges.add(edge); + edgeSet.add(((Number) id).longValue()); + } + } + } + } + } + } + + // Creating edges from each node to q long-range contacts + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + double sum = 0.0; + for (int k = 0; k < n; ++k) { + for (int l = 0; l < n; ++l) { + if (!isTorusBased() && d(i, j, k, l) > p) { + sum += Math.pow(d(i, j, k, l), -r); + } else if (isTorusBased() && dtb(i, j, k, l) > p) { + sum += Math.pow(dtb(i, j, k, l), -r); + } + + } + } + for (int m = 0; m < q; ++m) { + double b = random.nextDouble(); + boolean e = false; + while (!e) { + double pki = 0.0; + for (int k = 0; k < n && !e; ++k) { + for (int l = 0; l < n && !e; ++l) { + if (!isTorusBased() && d(i, j, k, l) > p || isTorusBased() && dtb(i, j, k, l) > p) { + pki += Math.pow(!isTorusBased() ? d(i, j, k, l) : dtb(i, j, k, l), -r) / sum; + Edge edge = factory.newEdge(nodes.get(i * n + j), nodes.get(k * n + l), 0, true); + Object id = edge.getId(); + if (id instanceof Number) { + if (b <= pki && !edgeSet.contains(((Number) id).longValue())) { + edges.add(edge); + edgeSet.add(((Number) id).longValue()); + e = true; + } + } + } + } + } + b = random.nextDouble(); + } + + } + } + } + return this; + } + + @Override + public KleinbergGraph commit() { + commitInner(); + return this; + } + + private int d(int i, int j, int k, int l) { + return Math.abs(k - i) + Math.abs(l - j); + } + + private int dtb(int i, int j, int k, int l) { + return Math.min(Math.abs(k - i), n - Math.abs(k - i)) + Math.min(Math.abs(l - j), n - Math.abs(l - j)); + } + + public int getn() { + return n; + } + + public int getp() { + return p; + } + + public int getq() { + return q; + } + + public int getr() { + return r; + } + + public boolean isTorusBased() { + return torusBased; + } + + public void setn(int n) { + this.n = n; + } + + public void setp(int p) { + this.p = p; + } + + public void setq(int q) { + this.q = q; + } + + public void setr(int r) { + this.r = r; + } + + public void setTorusBased(boolean torusBased) { + this.torusBased = torusBased; + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java new file mode 100644 index 00000000..c82dfa27 --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java @@ -0,0 +1,270 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark.benchmarks; + +import java.util.Random; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class LockingBenchmark { + + private final DataStruture struture = new DataStruture(); + private double number; + private final int READS = 500; + private final int WRITES = 100; + private final int READER_THREADS = 4; + private final int WRITER_THREADS = 4; + + public Runnable readWithoutLock() { + return () -> { + for (int i = 0; i < READS; i++) { + struture.read(); + } + }; + } + + public Runnable readWithLock() { + final ReadWriteLock lock = new ReentrantReadWriteLock(true); + return () -> { + for (int i = 0; i < READS; i++) { + lock.readLock().lock(); + struture.read(); + lock.readLock().unlock(); + } + }; + } + + public Runnable fairReadOnly() { + final ReadWriteLock lock = new ReentrantReadWriteLock(true); + return () -> { + Runnable r = () -> { + for (int i = 0; i < READS; i++) { + lock.readLock().lock(); + struture.read(); + lock.readLock().unlock(); + } + }; + Thread[] threads = new Thread[READER_THREADS]; + for (int i = 0; i < READER_THREADS; i++) { + Thread thread = new Thread(r); + thread.start(); + threads[i] = thread; + } + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException ex) { + Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); + } + } + }; + } + + public Runnable unfairReadOnly() { + final ReadWriteLock lock = new ReentrantReadWriteLock(false); + return () -> { + Runnable r = () -> { + for (int i = 0; i < READS; i++) { + lock.readLock().lock(); + struture.read(); + lock.readLock().unlock(); + } + }; + Thread[] threads = new Thread[READER_THREADS]; + for (int i = 0; i < READER_THREADS; i++) { + Thread thread = new Thread(r); + thread.start(); + threads[i] = thread; + } + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException ex) { + Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); + } + } + }; + } + + public Runnable fairWriteOnly() { + final ReadWriteLock lock = new ReentrantReadWriteLock(true); + return () -> { + Runnable r = () -> { + for (int i = 0; i < WRITES; i++) { + lock.writeLock().lock(); + struture.write(); + lock.writeLock().unlock(); + } + }; + Thread[] threads = new Thread[READER_THREADS]; + for (int i = 0; i < READER_THREADS; i++) { + Thread thread = new Thread(r); + thread.start(); + threads[i] = thread; + } + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException ex) { + Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); + } + } + }; + } + + public Runnable unfairWriteOnly() { + final ReadWriteLock lock = new ReentrantReadWriteLock(false); + return () -> { + Runnable r = () -> { + for (int i = 0; i < WRITES; i++) { + lock.writeLock().lock(); + struture.write(); + lock.writeLock().unlock(); + } + }; + Thread[] threads = new Thread[READER_THREADS]; + for (int i = 0; i < READER_THREADS; i++) { + Thread thread = new Thread(r); + thread.start(); + threads[i] = thread; + } + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException ex) { + Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); + } + } + }; + } + + public Runnable fairReadWrites() { + final ReadWriteLock lock = new ReentrantReadWriteLock(true); + return () -> { + Runnable reader = () -> { + for (int i = 0; i < READS; i++) { + lock.readLock().lock(); + struture.read(); + lock.readLock().unlock(); + } + }; + Runnable writer = () -> { + for (int i = 0; i < WRITES; i++) { + lock.writeLock().lock(); + struture.write(); + lock.writeLock().unlock(); + } + }; + Thread[] threads = new Thread[READER_THREADS + WRITER_THREADS]; + for (int i = 0; i < READER_THREADS; i++) { + Thread thread = new Thread(reader); + threads[i] = thread; + } + for (int i = 0; i < WRITER_THREADS; i++) { + Thread thread = new Thread(writer); + threads[READER_THREADS + i] = thread; + } + for (Thread thread : threads) { + thread.start(); + } + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException ex) { + Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); + } + } + }; + } + + public Runnable unfairReadWrites() { + final ReadWriteLock lock = new ReentrantReadWriteLock(false); + return () -> { + Runnable reader = () -> { + for (int i = 0; i < READS; i++) { + lock.readLock().lock(); + struture.read(); + lock.readLock().unlock(); + } + }; + Runnable writer = () -> { + for (int i = 0; i < WRITES; i++) { + lock.writeLock().lock(); + struture.write(); + lock.writeLock().unlock(); + } + }; + Thread[] threads = new Thread[READER_THREADS + WRITER_THREADS]; + for (int i = 0; i < READER_THREADS; i++) { + Thread thread = new Thread(reader); + threads[i] = thread; + } + for (int i = 0; i < WRITER_THREADS; i++) { + Thread thread = new Thread(writer); + threads[READER_THREADS + i] = thread; + } + for (Thread thread : threads) { + thread.start(); + } + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException ex) { + Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); + } + } + }; + } + + private class DataStruture { + + private final int[] values = new int[10000]; + private final int readLoops = 10; + private final int writeLoops = 5; + + public DataStruture() { + Random rand = new Random(454); + for (int i = 0; i < values.length; i++) { + values[i] = rand.nextInt(values.length); + } + } + + public void write() { + Random rand = new Random(45445); + for (int i = 0; i < writeLoops; i++) { + for (int j = 0; j < values.length; j++) { + values[j] = rand.nextInt(values.length); + } + } + } + + public void read() { + double avg = 0; + for (int i = 0; i < readLoops; i++) { + double sum = 0; + for (int j = 0; j < values.length; j++) { + sum += values[j]; + } + sum /= values.length; + avg += sum; + } + avg /= readLoops; + number = avg; + } + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java new file mode 100644 index 00000000..5aee547b --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark.benchmarks; + +import java.util.Iterator; +import java.util.List; + +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.Node; +import org.gephi.graph.impl.NodeImpl; +import org.gephi.graph.impl.NodeStore; + +/** + * + * @author mbastian, niteshbhargv + */ +public class NodeStoreBenchmark { + + private Object object; + + public Runnable iterateStore(final int nodes) { + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, 0, config).generate().commit(); + final NodeStore nodeStore = graph.getStore().getNodeStore(); + Runnable runnable = () -> { + Iterator m = nodeStore.iterator(); + for (; m.hasNext();) { + NodeImpl b = (NodeImpl) m.next(); + object = b; + } + }; + return runnable; + } + + public Runnable resetNodeStore(final int nodes) { + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, 0, config).generate().commit(); + final NodeStore nodeStore = graph.getStore().getNodeStore(); + final List nodeList = graph.getNodes(); + Runnable runnable = () -> { + for (Node n : nodeList) { + nodeStore.remove(n); + } + for (Node n : nodeList) { + nodeStore.add(n); + } + }; + return runnable; + } + + public Runnable pushStore(int nodes) { + final Configuration config = new Configuration(); + config.setEdgeIdType(Integer.class); + config.setNodeIdType(Integer.class); + final RandomGraph graph = new RandomGraph(nodes, 0, config).generate(); + final NodeStore nodeStore = graph.getStore().getNodeStore(); + final List nodeList = graph.getNodes(); + + Runnable runnable = () -> { + nodeStore.clear(); + for (Node n : nodeList) { + nodeStore.add(n); + } + }; + return runnable; + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java new file mode 100644 index 00000000..9d2e536e --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java @@ -0,0 +1,85 @@ +/** + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark.benchmarks; + +import java.util.Random; + +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Node; +import org.gephi.graph.impl.NodeImpl; + +/** + * Generates directed connected random graph with wiring probability p + * + * @author Mathieu Bastian, Nitesh Bhargava + */ +public class RandomGraph extends Generator { + + protected final int numberOfNodes; + protected final int numberOfEdges; + protected final double wiringProbability; + + public RandomGraph(int n, double p) { + this(n, p, new Configuration()); + } + + public RandomGraph(int n, double p, Configuration config) { + super(config); + numberOfNodes = n; + numberOfEdges = (int)(n*(n-1)*p); + wiringProbability = p; + } + + public RandomGraph(int nodes, int edges) { + this(nodes, edges, new Configuration()); + } + + public RandomGraph(int nodes, int edges, Configuration confi) { + this(nodes, ((double)edges)/(nodes*(nodes-1)), confi); + } + + @Override + public RandomGraph generate() { + Random random = new Random(); + + for (int i = 0; i < numberOfNodes; i++) { + Node node = factory.newNode(i); + nodes.add(node); + } + + if (wiringProbability > 0) { + for (int i = 0; i < numberOfNodes - 1; i++) { + NodeImpl source = graphStore.getNode(i); + for (int j = i + 1; j < numberOfNodes; j++) { + NodeImpl target = graphStore.getNode(j); + + if (random.nextDouble() < wiringProbability && source != target) { + Edge edge = factory.newEdge(source, target, 0, true); + edges.add(edge); + } + } + } + } + return this; + } + + @Override + public RandomGraph commit() { + commitInner(); + return this; + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java b/store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java new file mode 100644 index 00000000..e47c5f2e --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java @@ -0,0 +1,369 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark.nanobench; + +import java.lang.management.ManagementFactory; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Lightweight CPU and memory benchmarking utility.

Inspired from nanobench + * (http://code.google.com/p/nanobench/) + * + * @author mbastian + */ +public class NanoBench { + + public static NanoBench create() { + return new NanoBench(); + } + private static final Logger logger = Logger.getLogger(NanoBench.class.getSimpleName()); + private int numberOfMeasurement = 50; + private int numberOfWarmUp = 0; + private List listeners; + + public NanoBench() { + listeners = new ArrayList<>(2); + listeners.add(new CPUMeasure(logger)); + listeners.add(new MemoryUsage(logger)); + } + + public NanoBench measurements(int numberOfMeasurement) { + this.numberOfMeasurement = numberOfMeasurement; + return this; + } + + public NanoBench warmUps(int numberOfWarmups) { + this.numberOfWarmUp = numberOfWarmups; + return this; + } + + public NanoBench cpuAndMemory() { + listeners = new ArrayList<>(2); + listeners.add(new CPUMeasure(logger)); + listeners.add(new MemoryUsage(logger)); + return this; + } + + public static Logger getLogger() { + return logger; + } + + public NanoBench cpuOnly() { + listeners = new ArrayList<>(1); + listeners.add(new CPUMeasure(logger)); + return this; + } + + public NanoBench memoryOnly() { + listeners = new ArrayList<>(1); + listeners.add(new MemoryUsage(logger)); + return this; + } + + public void measure(String label, Runnable task) { + MemoryUtil.restoreJvm(); + doWarmup(task); + MemoryUtil.restoreJvm(); + stress(); + doMeasure(label, task); + stress(); + MemoryUtil.restoreJvm(); + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + static int[] arrayStress = new int[10000]; + + private void stress() { + int m = 0; + for (int j = 0; j < 100; j++) { + int dummy = 0; + for (int i = 1; i < arrayStress.length; i++) { + arrayStress[i] = (int) Math.round(Math.log(i)); + dummy += arrayStress[i - 1]; + } + m += dummy; + } + } + + private void doMeasure(String label, Runnable task) { + for (int i = 0; i < this.numberOfMeasurement; i++) { + TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState(label, i, this.numberOfMeasurement), task, listeners); + tmp.run(); + } + } + + private void doWarmup(Runnable task) { + for (int i = 0; i < this.numberOfWarmUp; i++) { + TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState("_warmup_", i, this.numberOfWarmUp), task, listeners); + tmp.run(); + } + } + + /** + * Decorated runnable which enables measurements. + */ + private static class TimeMeasureProxy implements Runnable { + + private MeasureState state; + private Runnable runnable; + private List listeners; + + public TimeMeasureProxy(MeasureState state, Runnable runnable, List listeners) { + super(); + this.state = state; + this.runnable = runnable; + this.listeners = listeners; + } + + @Override + public void run() { + this.state.startNow(); + this.runnable.run(); + this.state.endNow(); + if (!state.getLabel().equals("_warmup_")) { + notifyMeasurement(state); + } + } + + private void notifyMeasurement(MeasureState times) { + for (MeasureListener listener : this.listeners) { + listener.onMeasure(times); + } + } + } + + /** + * Interface for measure listeners. Measure listeners are called when a + * measurement is finished. + */ + private interface MeasureListener { + + void onMeasure(MeasureState state); + } + + /** + * Basic class to measure time spent in each measurement + */ + private static class MeasureState implements Comparable { + + private String label; + private long startTime; + private long endTime; + private long index; + private int measurement; + + public MeasureState(String label, long index, int measurement) { + super(); + this.label = label; + this.measurement = measurement; + this.index = index; + } + + public long getIndex() { + return index; + } + + public String getLabel() { + return label; + } + + public long getStartTime() { + return startTime; + } + + public long getEndTime() { + return endTime; + } + + public long getMeasurements() { + return measurement; + } + + public long getMeasureTime() { + return endTime - startTime; + } + + public void startNow() { + this.startTime = System.nanoTime(); + } + + public void endNow() { + this.endTime = System.nanoTime(); + } + + @Override + public int compareTo(MeasureState another) { + if (this.startTime > another.startTime) { + return -1; + } else if (this.startTime < another.startTime) { + return 1; + } else { + return 0; + } + } + } + + /** + * CPU time listener to calculate the average time spent in a measurement. + *

The listener is called at the end of each measurement and collect the + * time spent from the + * MeasureState instance. At the last measurement it shows the + * average time spent, the total time and the number of measurement per + * seconds. + */ + private static class CPUMeasure implements MeasureListener { + + private static final double BY_SECONDS = 1000000000.0; + private final Logger log; + private static final DecimalFormat decimalFormat = new DecimalFormat("#,##0.0000"); + private static final DecimalFormat integerFormat = new DecimalFormat("#,##0.0"); + private int count = 0; + private long timeUsed = 0; + + public CPUMeasure(Logger logger) { + this.log = logger; + } + + @Override + public void onMeasure(MeasureState state) { + count++; + outputMeasureInfo(state); + } + + private void outputMeasureInfo(MeasureState state) { + timeUsed += state.getMeasureTime(); + + if (isEnd(state)) { + long total = timeUsed; + + StringBuilder sb = new StringBuilder("\n"); + sb.append(state.getLabel()).append("\t").append("avg: ").append( + decimalFormat.format(total / state.getMeasurements() / 1000000.0)) + .append(" ms\t").append("total: ").append( + integerFormat.format(total / 1000000000.0)).append(" s\t").append( + " tps: ").append( + integerFormat.format(state.getMeasurements() + / (total / BY_SECONDS))).append("\t") + .append("running: ").append(count) + .append(" times"); + count = 0; + timeUsed = 0; + if (!state.getLabel().equals("_warmup_")) { + log.info(sb.toString()); + } + } + } + + private boolean isEnd(MeasureState state) { + return count == state.getMeasurements(); + } + } + + /** + * Memory usage listener to calculate the average memory usage.

The + * listener is called after each measurement and perform a full GC and + * calculate free memory. At the last measurement it shows the average + * memory usage. + */ + private static class MemoryUsage implements MeasureListener { + + private final Logger log; + private static final DecimalFormat integerFormat = new DecimalFormat("#,##0.000"); + private int count = 0; + private long memoryUsed = 0; + + public MemoryUsage(Logger logger) { + this.log = logger; + } + + @Override + public void onMeasure(MeasureState state) { + count++; + outputMeasureInfo(state); + } + + private void outputMeasureInfo(MeasureState state) { + MemoryUtil.restoreJvm(); + memoryUsed += MemoryUtil.memoryUsed(); + + if (isEnd(state)) { + StringBuilder sb = new StringBuilder("\n"); + sb.append("memory-usage: ").append(state.getLabel()).append("\t") + .append(format((memoryUsed / count) / (1024.0 * 1024.0))).append( + " Mb\n"); + count = 0; + memoryUsed = 0; + + if (!state.getLabel().equals("_warmup_")) { + log.info(sb.toString()); + } + } + } + + private String format(double value) { + return integerFormat.format(value); + } + + private boolean isEnd(MeasureState state) { + return count == state.getMeasurements(); + } + } + + /** + * Utility memory class to perform GC and calculate memory usage + */ + public static class MemoryUtil { + + /** + * Call GC until no more memory can be freed + */ + public static void restoreJvm() { + int maxRestoreJvmLoops = 10; + long memUsedPrev = memoryUsed(); + for (int i = 0; i < maxRestoreJvmLoops; i++) { + System.runFinalization(); + System.gc(); + + long memUsedNow = memoryUsed(); + // break early if have no more finalization and get constant mem used + if ((ManagementFactory.getMemoryMXBean() + .getObjectPendingFinalizationCount() == 0) + && (memUsedNow >= memUsedPrev)) { + break; + } else { + memUsedPrev = memUsedNow; + } + } + } + + /** + * Return the memory used in bytes + * + * @return heap memory used in bytes + */ + public static long memoryUsed() { + Runtime rt = Runtime.getRuntime(); + return rt.totalMemory() - rt.freeMemory(); + } + } +} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java b/store/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java new file mode 100644 index 00000000..9ec03293 --- /dev/null +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java @@ -0,0 +1,50 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.benchmark.util; + +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import org.testng.Reporter; + +/** + * Bridge between Java Logging API and TestNG's Reporter + * + * @author mbastian + */ +public class ReporterHandler extends Handler { + + @Override + public void publish(LogRecord record) { + String prefix = ""; + if (record.getLevel().equals(Level.INFO)) { + prefix = "[INFO] "; + } else if (record.getLevel().equals(Level.WARNING)) { + prefix = "[WARN] "; + } else if (record.getLevel().equals(Level.SEVERE)) { + prefix = "[SEVERE] "; + } + Reporter.log(prefix + record.getMessage() + "
", true); + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + } +} From 9aee92c9eee72445c365492f5afae89174d2fe27 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 16:05:13 +0100 Subject: [PATCH 024/271] Update formatter plugin --- store/pom.xml | 8 +- .../org/gephi/graph/api/AttributeUtils.java | 90 +++++++------- .../main/java/org/gephi/graph/api/Column.java | 7 +- .../org/gephi/graph/api/Configuration.java | 4 +- .../org/gephi/graph/api/DirectedGraph.java | 3 +- .../main/java/org/gephi/graph/api/Edge.java | 4 +- .../java/org/gephi/graph/api/Estimator.java | 3 +- .../main/java/org/gephi/graph/api/Graph.java | 7 +- .../java/org/gephi/graph/api/GraphBridge.java | 4 +- .../java/org/gephi/graph/api/GraphModel.java | 59 +++++----- .../org/gephi/graph/api/GraphObserver.java | 4 +- .../main/java/org/gephi/graph/api/Index.java | 4 +- .../java/org/gephi/graph/api/Interval.java | 16 +-- .../main/java/org/gephi/graph/api/Rect2D.java | 4 +- .../gephi/graph/api/TimeRepresentation.java | 5 +- .../java/org/gephi/graph/api/package.html | 11 +- .../graph/api/types/IntervalBooleanMap.java | 8 +- .../graph/api/types/IntervalByteMap.java | 8 +- .../graph/api/types/IntervalCharMap.java | 8 +- .../graph/api/types/IntervalDoubleMap.java | 8 +- .../graph/api/types/IntervalFloatMap.java | 8 +- .../graph/api/types/IntervalIntegerMap.java | 8 +- .../graph/api/types/IntervalLongMap.java | 8 +- .../gephi/graph/api/types/IntervalMap.java | 23 ++-- .../gephi/graph/api/types/IntervalSet.java | 20 ++-- .../graph/api/types/IntervalShortMap.java | 8 +- .../graph/api/types/IntervalStringMap.java | 4 +- .../org/gephi/graph/api/types/TimeSet.java | 4 +- .../graph/api/types/TimestampBooleanMap.java | 8 +- .../graph/api/types/TimestampByteMap.java | 8 +- .../graph/api/types/TimestampCharMap.java | 8 +- .../graph/api/types/TimestampDoubleMap.java | 8 +- .../graph/api/types/TimestampFloatMap.java | 8 +- .../graph/api/types/TimestampIntegerMap.java | 8 +- .../graph/api/types/TimestampLongMap.java | 8 +- .../gephi/graph/api/types/TimestampMap.java | 14 ++- .../gephi/graph/api/types/TimestampSet.java | 4 +- .../graph/api/types/TimestampShortMap.java | 8 +- .../graph/api/types/TimestampStringMap.java | 4 +- .../org/gephi/graph/api/types/package.html | 9 +- .../org/gephi/graph/impl/ArraysParser.java | 8 +- .../gephi/graph/impl/ColumnObserverImpl.java | 4 +- .../org/gephi/graph/impl/EdgeTypeStore.java | 8 +- .../org/gephi/graph/impl/ElementImpl.java | 44 +++---- .../graph/impl/FormattingAndParsingUtils.java | 29 +++-- .../gephi/graph/impl/GraphAttributesImpl.java | 5 +- .../gephi/graph/impl/GraphFactoryImpl.java | 16 +-- .../org/gephi/graph/impl/GraphModelImpl.java | 38 +++--- .../java/org/gephi/graph/impl/GraphStore.java | 21 ++-- .../gephi/graph/impl/GraphViewDecorator.java | 40 +++---- .../org/gephi/graph/impl/GraphViewImpl.java | 12 +- .../java/org/gephi/graph/impl/IndexImpl.java | 4 +- .../java/org/gephi/graph/impl/IndexStore.java | 8 +- .../gephi/graph/impl/Interval2IntTreeMap.java | 15 ++- .../org/gephi/graph/impl/IntervalsParser.java | 36 +++--- .../java/org/gephi/graph/impl/NodeStore.java | 3 +- .../org/gephi/graph/impl/NodesQuadTree.java | 3 +- .../org/gephi/graph/impl/TimeIndexStore.java | 4 +- .../gephi/graph/impl/TimestampIndexImpl.java | 4 +- .../gephi/graph/impl/TimestampsParser.java | 28 ++--- .../gephi/graph/impl/utils/LongPacker.java | 8 +- .../gephi/graph/impl/utils/MapDeepEquals.java | 4 +- .../java/org/gephi/graph/spi/LayoutData.java | 3 +- .../java/org/gephi/graph/spi/package.html | 9 +- .../graph/api/types/IntervalMapTest.java | 110 +++++++++++------- .../graph/api/types/IntervalSetTest.java | 61 ++++++---- .../graph/api/types/TimestampMapTest.java | 35 +++--- .../graph/api/types/TimestampSetTest.java | 24 ++-- .../gephi/graph/impl/ArraysParserTest.java | 5 +- .../gephi/graph/impl/AttributeUtilsTest.java | 98 ++++++++++------ .../org/gephi/graph/impl/BasicGraphStore.java | 12 +- .../org/gephi/graph/impl/EdgeImplTest.java | 12 +- .../org/gephi/graph/impl/ElementImplTest.java | 12 +- .../org/gephi/graph/impl/GraphGenerator.java | 46 ++++---- .../org/gephi/graph/impl/GraphModelTest.java | 6 +- .../gephi/graph/impl/IntervalsParserTest.java | 96 +++++++++------ .../gephi/graph/impl/SerializationTest.java | 107 +++++++++-------- .../org/gephi/graph/impl/TableImplTest.java | 6 +- .../graph/impl/TimestampsParserTest.java | 51 +++++--- 79 files changed, 818 insertions(+), 660 deletions(-) diff --git a/store/pom.xml b/store/pom.xml index 5388028e..5a322bc7 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -136,9 +136,9 @@ 1.18 - net.revelc.code + net.revelc.code.formatter formatter-maven-plugin - 0.5.2 + 2.17.0 org.codehaus.mojo @@ -275,11 +275,11 @@ - net.revelc.code + net.revelc.code.formatter formatter-maven-plugin ${project.basedir}/formatter-config.xml - true + diff --git a/store/src/main/java/org/gephi/graph/api/AttributeUtils.java b/store/src/main/java/org/gephi/graph/api/AttributeUtils.java index 520164e1..9a4f2b21 100644 --- a/store/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/store/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -341,8 +341,8 @@ public static String print(Object value, TimeFormat timeFormat, DateTimeZone tim * * @param str string to parse * @param typeClass class of the desired type - * @param timeZone time zone to use or null to use default time zone (UTC), - * for dynamic types only + * @param timeZone time zone to use or null to use default time zone (UTC), for + * dynamic types only * @return an instance of the type class, or null if str is null or * empty */ @@ -455,9 +455,11 @@ public static Object parse(String str, Class typeClass, DateTimeZone timeZone) { } else if (typeClass.equals(double[].class)) { return ArraysParser.parseArrayAsPrimitiveArray(Double[].class, str); } else if (typeClass.equals(Boolean[].class) || typeClass.equals(String[].class) || typeClass - .equals(Character[].class) || typeClass.equals(Byte[].class) || typeClass.equals(Short[].class) || typeClass - .equals(Integer[].class) || typeClass.equals(Long[].class) || typeClass.equals(Float[].class) || typeClass - .equals(Double[].class) || typeClass.equals(BigInteger[].class) || typeClass.equals(BigDecimal[].class)) { + .equals(Character[].class) || typeClass.equals(Byte[].class) || typeClass + .equals(Short[].class) || typeClass.equals(Integer[].class) || typeClass + .equals(Long[].class) || typeClass.equals(Float[].class) || typeClass + .equals(Double[].class) || typeClass + .equals(BigInteger[].class) || typeClass.equals(BigDecimal[].class)) { return ArraysParser.parseArray(typeClass, str); } @@ -517,15 +519,15 @@ public static Class getPrimitiveType(Class type) { * * @param array wrapped primitive array instance * @return primitive array instance - * @throws IllegalArgumentException Thrown if any of the array values is - * null + * @throws IllegalArgumentException Thrown if any of the array values is null */ public static Object getPrimitiveArray(Object[] array) { if (!isSupported(array.getClass())) { throw new IllegalArgumentException("Unsupported type " + array.getClass().getCanonicalName()); } Class arrayClass = array.getClass().getComponentType(); - if (!arrayClass.isPrimitive() && (arrayClass == Double.class || arrayClass == Float.class || arrayClass == Long.class || arrayClass == Integer.class || arrayClass == Short.class || arrayClass == Character.class || arrayClass == Byte.class || arrayClass == Boolean.class)) { + if (!arrayClass + .isPrimitive() && (arrayClass == Double.class || arrayClass == Float.class || arrayClass == Long.class || arrayClass == Integer.class || arrayClass == Short.class || arrayClass == Character.class || arrayClass == Byte.class || arrayClass == Boolean.class)) { Class primitiveClass = getPrimitiveType(arrayClass); int arrayLength = array.length; @@ -744,8 +746,8 @@ private static List getStandardizedList(List list) { } } if (oCls != null && !(isSimpleType(oCls) || isArrayType(oCls))) { - throw new IllegalArgumentException("The list contains unsupported type " + oCls.getClass() - .getCanonicalName()); + throw new IllegalArgumentException( + "The list contains unsupported type " + oCls.getClass().getCanonicalName()); } if (oCls != null) { if (oCls.equals(Integer.class)) { @@ -790,8 +792,8 @@ private static Set getStandardizedSet(Set set) { } } if (oCls != null && !(isSimpleType(oCls) || isArrayType(oCls))) { - throw new IllegalArgumentException("The set contains unsupported type " + oCls.getClass() - .getCanonicalName()); + throw new IllegalArgumentException( + "The set contains unsupported type " + oCls.getClass().getCanonicalName()); } if (oCls != null) { if (oCls.equals(Integer.class)) { @@ -837,13 +839,13 @@ private static Map getStandardizedMap(Map map) { } } if (value != null && !(isSimpleType(value.getClass()) || isArrayType(value.getClass()))) { - throw new IllegalArgumentException("The map contains unsupported value type " + value.getClass() - .getCanonicalName()); + throw new IllegalArgumentException( + "The map contains unsupported value type " + value.getClass().getCanonicalName()); } } if (oCls != null && !isSimpleType(oCls)) { - throw new IllegalArgumentException("The map contains unsupported key type " + oCls.getClass() - .getCanonicalName()); + throw new IllegalArgumentException( + "The map contains unsupported key type " + oCls.getClass().getCanonicalName()); } if (oCls != null) { if (oCls.equals(Integer.class)) { @@ -883,14 +885,20 @@ public static boolean isNumberType(Class type) { } type = getStandardizedType(type); return Number.class.isAssignableFrom(type) || int[].class.isAssignableFrom(type) || float[].class - .isAssignableFrom(type) || double[].class.isAssignableFrom(type) || byte[].class.isAssignableFrom(type) || short[].class - .isAssignableFrom(type) || long[].class.isAssignableFrom(type) || type - .equals(TimestampIntegerMap.class) || type.equals(TimestampFloatMap.class) || type - .equals(TimestampDoubleMap.class) || type.equals(TimestampLongMap.class) || type - .equals(TimestampShortMap.class) || type.equals(TimestampByteMap.class) || type - .equals(IntervalIntegerMap.class) || type.equals(IntervalFloatMap.class) || type - .equals(IntervalDoubleMap.class) || type.equals(IntervalLongMap.class) || type - .equals(IntervalShortMap.class) || type.equals(IntervalByteMap.class); + .isAssignableFrom(type) || double[].class.isAssignableFrom(type) || byte[].class + .isAssignableFrom(type) || short[].class.isAssignableFrom(type) || long[].class + .isAssignableFrom(type) || type.equals(TimestampIntegerMap.class) || type + .equals(TimestampFloatMap.class) || type + .equals(TimestampDoubleMap.class) || type + .equals(TimestampLongMap.class) || type + .equals(TimestampShortMap.class) || type + .equals(TimestampByteMap.class) || type + .equals(IntervalIntegerMap.class) || type + .equals(IntervalFloatMap.class) || type + .equals(IntervalDoubleMap.class) || type + .equals(IntervalLongMap.class) || type + .equals(IntervalShortMap.class) || type + .equals(IntervalByteMap.class); } /** @@ -922,8 +930,8 @@ public static boolean isBooleanType(Class type) { throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName()); } type = getStandardizedType(type); - return type.equals(Boolean.class) || type.equals(boolean[].class) || type.equals(TimestampBooleanMap.class) || type - .equals(IntervalBooleanMap.class); + return type.equals(Boolean.class) || type.equals(boolean[].class) || type + .equals(TimestampBooleanMap.class) || type.equals(IntervalBooleanMap.class); } /** @@ -935,7 +943,7 @@ public static boolean isBooleanType(Class type) { public static boolean isDynamicType(Class type) { return (!type.equals(TimestampMap.class) && TimestampMap.class.isAssignableFrom(type)) || type .equals(TimestampSet.class) || (!type.equals(IntervalMap.class) && IntervalMap.class - .isAssignableFrom(type)) || type.equals(IntervalSet.class); + .isAssignableFrom(type)) || type.equals(IntervalSet.class); } /** @@ -947,7 +955,8 @@ public static boolean isDynamicType(Class type) { * @return true if type is a simple type, false otherwise */ public static boolean isSimpleType(Class type) { - return (type.isPrimitive() && type != void.class) || type == Double.class || type == Float.class || type == Long.class || type == Integer.class || type == Short.class || type == Character.class || type == Byte.class || type == Boolean.class || type == String.class; + return (type + .isPrimitive() && type != void.class) || type == Double.class || type == Float.class || type == Long.class || type == Integer.class || type == Short.class || type == Character.class || type == Byte.class || type == Boolean.class || type == String.class; } /** @@ -1010,8 +1019,8 @@ public static double parseDateTime(String dateTime, DateTimeZone timeZone) { } /** - * Parses the given time and returns its milliseconds representation. - * Default time zone is used (UTC). + * Parses the given time and returns its milliseconds representation. Default + * time zone is used (UTC). * * @param dateTime the type to parse * @return milliseconds representation @@ -1069,8 +1078,8 @@ public static String printDate(double timestamp, DateTimeZone timeZone) { } /** - * Returns the date's string representation of the given timestamp. Default - * time zone is used (UTC). + * Returns the date's string representation of the given timestamp. Default time + * zone is used (UTC). * * @param timestamp time, in milliseconds * @return formatted date @@ -1094,8 +1103,8 @@ public static String printDateTime(double timestamp, DateTimeZone timeZone) { } /** - * Returns the time's tring representation of the given timestamp. Default - * time zone is used (UTC). + * Returns the time's tring representation of the given timestamp. Default time + * zone is used (UTC). * * @param timestamp time, in milliseconds * @return formatted time @@ -1105,8 +1114,7 @@ public static String printDateTime(double timestamp) { } /** - * Returns the string representation of the given timestamp in the given - * format. + * Returns the string representation of the given timestamp in the given format. * * @param timestamp time, in milliseconds * @param timeFormat time format @@ -1127,8 +1135,8 @@ public static String printTimestampInFormat(double timestamp, TimeFormat timeFor } /** - * Returns the string representation of the given timestamp in the given - * format. Default time zone is used (UTC). + * Returns the string representation of the given timestamp in the given format. + * Default time zone is used (UTC). * * @param timestamp time, in milliseconds * @param timeFormat time format @@ -1139,9 +1147,9 @@ public static String printTimestampInFormat(double timestamp, TimeFormat timeFor } /** - * Returns the string representation of the given array. The used format is - * the same format supported by - * {@link #parse(java.lang.String, java.lang.Class)} method + * Returns the string representation of the given array. The used format is the + * same format supported by {@link #parse(java.lang.String, java.lang.Class)} + * method * * @param arr Input array. Can be an array of objects or primitives. * @return formatted array diff --git a/store/src/main/java/org/gephi/graph/api/Column.java b/store/src/main/java/org/gephi/graph/api/Column.java index 45f75e14..13f87fcd 100644 --- a/store/src/main/java/org/gephi/graph/api/Column.java +++ b/store/src/main/java/org/gephi/graph/api/Column.java @@ -33,8 +33,8 @@ public interface Column { public String getId(); /** - * Returns the column's integer index, which is the position of the column - * in the store. + * Returns the column's integer index, which is the position of the column in + * the store. * * @return the column's index */ @@ -106,8 +106,7 @@ public interface Column { /** * Returns true if this column is a property. *

- * This is equivalent to test if the column's origin is - * Origin.PROPERTY + * This is equivalent to test if the column's origin is Origin.PROPERTY * * @return true if property, false otherwise */ diff --git a/store/src/main/java/org/gephi/graph/api/Configuration.java b/store/src/main/java/org/gephi/graph/api/Configuration.java index d14223cc..adca5dbb 100644 --- a/store/src/main/java/org/gephi/graph/api/Configuration.java +++ b/store/src/main/java/org/gephi/graph/api/Configuration.java @@ -141,8 +141,8 @@ public Class getEdgeWeightType() { * @throws IllegalArgumentException if the type isn't supported */ public void setEdgeWeightType(Class edgeWeightType) { - if (Double.class.equals(edgeWeightType) || TimestampDoubleMap.class.equals(edgeWeightType) || IntervalDoubleMap.class - .equals(edgeWeightType)) { + if (Double.class.equals(edgeWeightType) || TimestampDoubleMap.class + .equals(edgeWeightType) || IntervalDoubleMap.class.equals(edgeWeightType)) { this.edgeWeightType = edgeWeightType; } else { throw new IllegalArgumentException("Unsupported type " + edgeWeightType.getClass().getCanonicalName()); diff --git a/store/src/main/java/org/gephi/graph/api/DirectedGraph.java b/store/src/main/java/org/gephi/graph/api/DirectedGraph.java index b34f80c1..6ea13a46 100644 --- a/store/src/main/java/org/gephi/graph/api/DirectedGraph.java +++ b/store/src/main/java/org/gephi/graph/api/DirectedGraph.java @@ -34,8 +34,7 @@ public interface DirectedGraph extends Graph { public Edge getEdge(Node source, Node target); /** - * Gets the edge adjacent to source and target with an edge of the given - * type. + * Gets the edge adjacent to source and target with an edge of the given type. * * @param source the source node * @param target the target node diff --git a/store/src/main/java/org/gephi/graph/api/Edge.java b/store/src/main/java/org/gephi/graph/api/Edge.java index e0d596ff..307e8619 100644 --- a/store/src/main/java/org/gephi/graph/api/Edge.java +++ b/store/src/main/java/org/gephi/graph/api/Edge.java @@ -62,8 +62,8 @@ public interface Edge extends Element, EdgeProperties { /** * Returns the edge's weight in the given graph view. *

- * Views can configure a time interval and therefore the edge weight over - * time may vary. + * Views can configure a time interval and therefore the edge weight over time + * may vary. * * @param view graph view * @return weight diff --git a/store/src/main/java/org/gephi/graph/api/Estimator.java b/store/src/main/java/org/gephi/graph/api/Estimator.java index 9eb8cc6c..a2d502ea 100644 --- a/store/src/main/java/org/gephi/graph/api/Estimator.java +++ b/store/src/main/java/org/gephi/graph/api/Estimator.java @@ -59,8 +59,7 @@ public boolean is(Estimator estimator) { } /** - * Returns true if this estimator is any of the given - * estimators. + * Returns true if this estimator is any of the given estimators. * * @param estimators estimators to test equality * @return true if estimators contains this estimator diff --git a/store/src/main/java/org/gephi/graph/api/Graph.java b/store/src/main/java/org/gephi/graph/api/Graph.java index e2e698e7..2ba7d1a7 100644 --- a/store/src/main/java/org/gephi/graph/api/Graph.java +++ b/store/src/main/java/org/gephi/graph/api/Graph.java @@ -303,14 +303,13 @@ public interface Graph { public boolean isAdjacent(Node node1, Node node2); /** - * Returns true if node1 and node2 are adjacent with an edge of the given - * type. + * Returns true if node1 and node2 are adjacent with an edge of the given type. * * @param node1 the first node * @param node2 the second node * @param type the edge type - * @return true if node1 and node2 are adjacent with an edge og the given - * type, false otherwise + * @return true if node1 and node2 are adjacent with an edge og the given type, + * false otherwise */ public boolean isAdjacent(Node node1, Node node2, int type); diff --git a/store/src/main/java/org/gephi/graph/api/GraphBridge.java b/store/src/main/java/org/gephi/graph/api/GraphBridge.java index d2c1f898..3ee26087 100644 --- a/store/src/main/java/org/gephi/graph/api/GraphBridge.java +++ b/store/src/main/java/org/gephi/graph/api/GraphBridge.java @@ -34,8 +34,8 @@ public interface GraphBridge { * All edges attached to nodes will be copied as well if their * source and target exists in this graph store. *

- * This operation takes care of copying attribute columns and values, edge - * type labels and element properties. + * This operation takes care of copying attribute columns and values, edge type + * labels and element properties. *

* Beware that the source's configuration should match this graph store * configuration. diff --git a/store/src/main/java/org/gephi/graph/api/GraphModel.java b/store/src/main/java/org/gephi/graph/api/GraphModel.java index b76bcf89..4d25d5a1 100644 --- a/store/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/store/src/main/java/org/gephi/graph/api/GraphModel.java @@ -78,8 +78,7 @@ *

* For performance reasons, edge labels are internally represented as integers * and the mapping between arbitrary labels is managed through the - * {@link #addEdgeType(java.lang.Object) - * } and + * {@link #addEdgeType(java.lang.Object) } and * {@link #getEdgeType(java.lang.Object) } methods. By default, edges have a * null label, which is internally represented as zero. * @@ -139,10 +138,9 @@ public static GraphModel read(DataInput input) throws IOException { } /** - * Read the input and return the read graph model without - * an explicit version header in the input. To be used with old - * graphstore serialized data prior to version 0.4 (first, that added - * the version header). + * Read the input and return the read graph model without an + * explicit version header in the input. To be used with old graphstore + * serialized data prior to version 0.4 (first, that added the version header). * * @param input data input to read from * @param graphStoreVersion Forced version to use @@ -361,9 +359,8 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce /** * Creates a new graph view. *

- * The node and edge parameters allows to restrict the view filtering to - * only nodes or only edges. By default, the view applies to both nodes and - * edges. + * The node and edge parameters allows to restrict the view filtering to only + * nodes or only edges. By default, the view applies to both nodes and edges. * * @param node true to enable node view, false otherwise * @param edge true to enable edge view, false otherwise @@ -382,9 +379,8 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce /** * Creates a new graph based on an existing view. *

- * The node and edge parameters allows to restrict the view filtering to - * only nodes or only edges. By default, the view applies to both nodes and - * edges. + * The node and edge parameters allows to restrict the view filtering to only + * nodes or only edges. By default, the view applies to both nodes and edges. * * @param view view to copy * @param node true to enable node view, false otherwise @@ -403,8 +399,7 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce /** * Sets the given time interval to the view. *

- * Each view can be configured with a time interval to filter a graph over - * time. + * Each view can be configured with a time interval to filter a graph over time. * * @param view the view to configure * @param interval the time interval @@ -412,22 +407,22 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce public void setTimeInterval(GraphView view, Interval interval); /** - * Returns the node table. Contains all the columns associated to - * node elements. + * Returns the node table. Contains all the columns associated to node + * elements. *

- * A GraphModel always has node and edge tables - * by default. + * A GraphModel always has node and edge tables by + * default. * * @return node table, contains node columns */ public Table getNodeTable(); /** - * Returns the edge table. Contains all the columns associated to - * edge elements. + * Returns the edge table. Contains all the columns associated to edge + * elements. *

- * A GraphModel always has node and edge tables - * by default. + * A GraphModel always has node and edge tables by + * default. * * @return edge table, contains edge columns */ @@ -513,8 +508,8 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce /** * Gets the time bounds. *

- * The time bounds is an interval made of the minimum and maximum time - * observed in the entire graph. + * The time bounds is an interval made of the minimum and maximum time observed + * in the entire graph. * * @return time bounds */ @@ -523,8 +518,8 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce /** * Gets the time bounds for the visible graph. *

- * The time bounds is an interval made of the minimum and maximum time - * observed in the entire graph. + * The time bounds is an interval made of the minimum and maximum time observed + * in the entire graph. * * @return time bounds */ @@ -533,8 +528,8 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce /** * Gets the time bounds for the given graph view. *

- * The time bounds is an interval made of the minimum and maximum time - * observed in the entire graph. + * The time bounds is an interval made of the minimum and maximum time observed + * in the entire graph. * * @param view the graph view * @return time bounds @@ -608,8 +603,8 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce *

* Each node has a unique store identifier which can be retrieved from * {@link Node#getStoreId() }. This maximum number can help design algorithms - * thar rely on storing nodes in a array. Note that not all consecutive ids - * may be assigned. + * thar rely on storing nodes in a array. Note that not all consecutive ids may + * be assigned. * * @return maximum node store id */ @@ -620,8 +615,8 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce *

* Each edge has a unique store identifier which can be retrieved from * {@link Edge#getStoreId() }. This maximum number can help design algorithms - * thar rely on storing edges in a array. Note that not all consecutive ids - * may be assigned. + * thar rely on storing edges in a array. Note that not all consecutive ids may + * be assigned. * * @return maximum edge store id */ diff --git a/store/src/main/java/org/gephi/graph/api/GraphObserver.java b/store/src/main/java/org/gephi/graph/api/GraphObserver.java index 221e961a..a5e35330 100644 --- a/store/src/main/java/org/gephi/graph/api/GraphObserver.java +++ b/store/src/main/java/org/gephi/graph/api/GraphObserver.java @@ -77,8 +77,8 @@ public interface GraphObserver { public boolean isDestroyed(); /** - * Returns true if this observer has never got its - * hasGraphChanged() method called. + * Returns true if this observer has never got its hasGraphChanged() + * method called. * * @return true if new observer, false otherwise */ diff --git a/store/src/main/java/org/gephi/graph/api/Index.java b/store/src/main/java/org/gephi/graph/api/Index.java index d9d6e5ee..a0de16c4 100644 --- a/store/src/main/java/org/gephi/graph/api/Index.java +++ b/store/src/main/java/org/gephi/graph/api/Index.java @@ -42,8 +42,8 @@ public interface Index { * * @param column the column to get values * @param value the value - * @return an iterable with element with value in column, - * or null if value not found + * @return an iterable with element with value in column, or + * null if value not found */ public Iterable get(Column column, Object value); diff --git a/store/src/main/java/org/gephi/graph/api/Interval.java b/store/src/main/java/org/gephi/graph/api/Interval.java index b7bf950d..df6becf5 100644 --- a/store/src/main/java/org/gephi/graph/api/Interval.java +++ b/store/src/main/java/org/gephi/graph/api/Interval.java @@ -71,15 +71,15 @@ public Interval(double low, double high) { * *

* Note that if two intervals are equal ({@code i.low = i'.low} and - * {@code i.high = i'.high}), they overlap as well. But if they simply - * overlap (for instance {@code i.low < i'.low} and {@code i.high > + * {@code i.high = i'.high}), they overlap as well. But if they simply overlap + * (for instance {@code i.low < i'.low} and {@code i.high > * i'.high}) they aren't equal. * * @param interval the interval to be compared * - * @return a negative integer, zero, or a positive integer as this interval - * is to the left of, overlaps with, or is to the right of the - * specified interval. + * @return a negative integer, zero, or a positive integer as this interval is + * to the left of, overlaps with, or is to the right of the specified + * interval. * * @throws NullPointerException if {@code interval} is null. */ @@ -101,9 +101,9 @@ public int compareTo(Interval interval) { * Compares this interval to the given timetamp. * * @param timestamp timestamp - * @return a negative integer, zero or a positive integer if this interval - * is to the left of, overlaps with, or is to the right with the - * specified timestamp. + * @return a negative integer, zero or a positive integer if this interval is to + * the left of, overlaps with, or is to the right with the specified + * timestamp. * * @throws NullPointerException if {@code timestamp} is null. */ diff --git a/store/src/main/java/org/gephi/graph/api/Rect2D.java b/store/src/main/java/org/gephi/graph/api/Rect2D.java index ad02f078..f7d89025 100644 --- a/store/src/main/java/org/gephi/graph/api/Rect2D.java +++ b/store/src/main/java/org/gephi/graph/api/Rect2D.java @@ -78,8 +78,8 @@ public String toString() { } public String toString(NumberFormat formatter) { - return "(" + formatter.format(minX) + " " + formatter.format(minY) + ") < " + "(" + formatter.format(maxX) + " " + formatter - .format(maxY) + ")"; + return "(" + formatter.format(minX) + " " + formatter.format(minY) + ") < " + "(" + formatter + .format(maxX) + " " + formatter.format(maxY) + ")"; } public boolean contains(Rect2D rect) { diff --git a/store/src/main/java/org/gephi/graph/api/TimeRepresentation.java b/store/src/main/java/org/gephi/graph/api/TimeRepresentation.java index 280e8a04..a7201f93 100644 --- a/store/src/main/java/org/gephi/graph/api/TimeRepresentation.java +++ b/store/src/main/java/org/gephi/graph/api/TimeRepresentation.java @@ -41,9 +41,8 @@ public enum TimeRepresentation { /** * Interval representation (continuous). *

- * Time is represented using intervals, with a beginning and an end. - * Intervals are always included on both bounds but allows an infinite - * bound. + * Time is represented using intervals, with a beginning and an end. Intervals + * are always included on both bounds but allows an infinite bound. */ INTERVAL; } diff --git a/store/src/main/java/org/gephi/graph/api/package.html b/store/src/main/java/org/gephi/graph/api/package.html index 4b6171f6..2b0d4e8f 100644 --- a/store/src/main/java/org/gephi/graph/api/package.html +++ b/store/src/main/java/org/gephi/graph/api/package.html @@ -1,3 +1,8 @@ - - Complete API description, where GraphModel is the entry point. - + + + + Complete API description, where + GraphModel + is the entry point. + + \ No newline at end of file diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java index 7f962d52..0d9898c2 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java @@ -38,8 +38,8 @@ public IntervalBooleanMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -98,8 +98,8 @@ public boolean getBoolean(Interval interval, boolean defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java index 80b25d38..9601e71e 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java @@ -38,8 +38,8 @@ public IntervalByteMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -98,8 +98,8 @@ public byte getByte(Interval interval, byte defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java index 5353cdec..054579d3 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java @@ -38,8 +38,8 @@ public IntervalCharMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -98,8 +98,8 @@ public char getCharacter(Interval interval, char defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java index ae29a31f..fe209193 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java @@ -38,8 +38,8 @@ public IntervalDoubleMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -98,8 +98,8 @@ public double getDouble(Interval interval, double defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java index 15dd5e85..8075bfe7 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java @@ -39,8 +39,8 @@ public IntervalFloatMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -99,8 +99,8 @@ public float getFloat(Interval interval, float defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java index 4ccbc4e6..42f717e6 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java @@ -38,8 +38,8 @@ public IntervalIntegerMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -98,8 +98,8 @@ public int getInteger(Interval interval, int defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java index 40d397e6..d25779da 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java @@ -38,8 +38,8 @@ public IntervalLongMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -98,8 +98,8 @@ public long getLong(Interval interval, long defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalMap.java index adbcdc88..67c917af 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalMap.java @@ -52,8 +52,8 @@ public IntervalMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -283,8 +283,8 @@ protected int removeInner(double intervalStart, double intervalEnd) { if (startValue == intervalStart && endValue > intervalEnd) { return -1; } - if ((shift = (intervalEnd > endValue ? 2 : intervalEnd < endValue ? -2 : intervalStart > startValue ? 2 - : 0)) == 0) { + if ((shift = (intervalEnd > endValue ? 2 + : intervalEnd < endValue ? -2 : intervalStart > startValue ? 2 : 0)) == 0) { if (removeIndex == realSize - 2) { size--; } else { @@ -337,8 +337,8 @@ protected int getIndex(double intervalStart, double intervalEnd) { if (startValue == intervalStart && endValue > intervalEnd) { return -1; } - if ((shift = (intervalEnd > endValue ? 2 : intervalEnd < endValue ? -2 : intervalStart > startValue ? 2 - : 0)) == 0) { + if ((shift = (intervalEnd > endValue ? 2 + : intervalEnd < endValue ? -2 : intervalStart > startValue ? 2 : 0)) == 0) { return foundIndex; } } @@ -418,11 +418,11 @@ public Interval[] toKeysArray() { /** * Returns an array of all intervals in this set. *

- * The intervals are represented in a flat and sorted array (e.g. - * {[1.0,2.0], [5.0,6.0]}) returns [1.0,2.0,5.0,6.0]). + * The intervals are represented in a flat and sorted array (e.g. {[1.0,2.0], + * [5.0,6.0]}) returns [1.0,2.0,5.0,6.0]). *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all intervals */ @@ -621,7 +621,8 @@ public String toString(TimeFormat timeFormat, DateTimeZone timeZone) { sb.append(", "); String stringValue = values[i].toString(); - if (FormattingAndParsingUtils.containsDynamicSpecialCharacters(stringValue) || stringValue.trim().isEmpty()) { + if (FormattingAndParsingUtils.containsDynamicSpecialCharacters(stringValue) || stringValue.trim() + .isEmpty()) { sb.append('"'); sb.append(stringValue.replace("\\", "\\\\").replace("\"", "\\\"")); sb.append('"'); diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalSet.java b/store/src/main/java/org/gephi/graph/api/types/IntervalSet.java index ef3210e3..5d980ddc 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalSet.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalSet.java @@ -42,8 +42,8 @@ public IntervalSet() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * intervals is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of intervals is + * known in advance as it minimizes array resizes. * * @param capacity interval capacity */ @@ -114,8 +114,8 @@ public boolean contains(Interval interval) { if (startValue == interval.getLow() && endValue > interval.getHigh()) { return false; } - if ((shift = (interval.getHigh() > endValue ? 2 : interval.getHigh() < endValue ? -2 : interval - .getLow() > startValue ? 2 : 0)) == 0) { + if ((shift = (interval.getHigh() > endValue ? 2 + : interval.getHigh() < endValue ? -2 : interval.getLow() > startValue ? 2 : 0)) == 0) { return true; } } @@ -126,11 +126,11 @@ public boolean contains(Interval interval) { /** * Returns an array of all intervals in this set in a flat format. *

- * The intervals are represented in a flat and sorted array (e.g. - * {[1.0,2.0], [5.0,6.0]}) returns [1.0,2.0,5.0,6.0]). + * The intervals are represented in a flat and sorted array (e.g. {[1.0,2.0], + * [5.0,6.0]}) returns [1.0,2.0,5.0,6.0]). *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all intervals */ @@ -262,8 +262,8 @@ private int removeInner(double intervalStart, double intervalEnd) { if (startValue == intervalStart && endValue > intervalEnd) { return -1; } - if ((shift = (intervalEnd > endValue ? 2 : intervalEnd < endValue ? -2 : intervalStart > startValue ? 2 - : 0)) == 0) { + if ((shift = (intervalEnd > endValue ? 2 + : intervalEnd < endValue ? -2 : intervalStart > startValue ? 2 : 0)) == 0) { if (removeIndex == realSize - 2) { size--; } else { diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java index 62c04daf..ce6eedcf 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java @@ -38,8 +38,8 @@ public IntervalShortMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -98,8 +98,8 @@ public short getShort(Interval interval, short defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java b/store/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java index cbbdd84a..968833f3 100644 --- a/store/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java @@ -37,8 +37,8 @@ public IntervalStringMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimeSet.java b/store/src/main/java/org/gephi/graph/api/types/TimeSet.java index 43469aea..14368697 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimeSet.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimeSet.java @@ -67,8 +67,8 @@ public interface TimeSet { /** * Returns an array of all keys in this set. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all keys */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java index f9555766..38621d01 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java @@ -39,8 +39,8 @@ public TimestampBooleanMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -157,8 +157,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java index d0f41008..76f95176 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java @@ -38,8 +38,8 @@ public TimestampByteMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -120,8 +120,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java index ce3caa53..355e84a4 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java @@ -39,8 +39,8 @@ public TimestampCharMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -153,8 +153,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java index f6e4f1f5..eebf197a 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java @@ -37,8 +37,8 @@ public TimestampDoubleMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -101,8 +101,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java index a5215e11..18ab8886 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java @@ -39,8 +39,8 @@ public TimestampFloatMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -121,8 +121,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java index dbf90b45..7fb42030 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java @@ -39,8 +39,8 @@ public TimestampIntegerMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -115,8 +115,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java index 660321e9..db37c662 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java @@ -39,8 +39,8 @@ public TimestampLongMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -115,8 +115,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampMap.java index f3f1b0f5..21b3792b 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampMap.java @@ -52,8 +52,8 @@ public TimestampMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -267,8 +267,8 @@ public boolean contains(Double timestamp) { /** * Returns an array of all timestamps in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all timestamps */ @@ -331,7 +331,8 @@ public boolean equals(Object obj) { } Object o1 = this.getValue(i); Object o2 = other.getValue(i); - if ((o1 == null && o2 != null) || (o1 != null && o2 == null) || (o1 != null && o2 != null && !o1.equals(o2))) { + if ((o1 == null && o2 != null) || (o1 != null && o2 == null) || (o1 != null && o2 != null && !o1 + .equals(o2))) { return false; } } @@ -469,7 +470,8 @@ public String toString(TimeFormat timeFormat, DateTimeZone timeZone) { sb.append(", "); String stringValue = values[i].toString(); - if (FormattingAndParsingUtils.containsDynamicSpecialCharacters(stringValue) || stringValue.trim().isEmpty()) { + if (FormattingAndParsingUtils.containsDynamicSpecialCharacters(stringValue) || stringValue.trim() + .isEmpty()) { sb.append('"'); sb.append(stringValue.replace("\\", "\\\\").replace("\"", "\\\"")); sb.append('"'); diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampSet.java b/store/src/main/java/org/gephi/graph/api/types/TimestampSet.java index 1c0074b4..f700379e 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampSet.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampSet.java @@ -41,8 +41,8 @@ public TimestampSet() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java index 94a77295..d2881844 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java @@ -39,8 +39,8 @@ public TimestampShortMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ @@ -115,8 +115,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients - * should make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should + * make a copy if the array is written to. * * @return array of all values */ diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java b/store/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java index 28bbfdb1..694365dd 100644 --- a/store/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java +++ b/store/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java @@ -37,8 +37,8 @@ public TimestampStringMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of - * timestamps is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps + * is known in advance as it minimizes array resizes. * * @param capacity timestamp capacity */ diff --git a/store/src/main/java/org/gephi/graph/api/types/package.html b/store/src/main/java/org/gephi/graph/api/types/package.html index 75e3f2a5..5c4e1bfd 100644 --- a/store/src/main/java/org/gephi/graph/api/types/package.html +++ b/store/src/main/java/org/gephi/graph/api/types/package.html @@ -1,3 +1,6 @@ - - Custom types the API supports, in addition of primitive and arrays. - + + + + Custom types the API supports, in addition of primitive and arrays. + + \ No newline at end of file diff --git a/store/src/main/java/org/gephi/graph/impl/ArraysParser.java b/store/src/main/java/org/gephi/graph/impl/ArraysParser.java index 03d69d1b..f0863955 100644 --- a/store/src/main/java/org/gephi/graph/impl/ArraysParser.java +++ b/store/src/main/java/org/gephi/graph/impl/ArraysParser.java @@ -128,13 +128,13 @@ public static T[] parseArray(Class arrayTypeClass, String input) throws /** * Parses an array of any primitive type. * - * @param Primitive type wrapper. For example Integer for int array or - * Long for long array. + * @param Primitive type wrapper. For example Integer for int array or Long + * for long array. * @param arrayTypeClass Array type to parse * @param input Input string to parse * @return Parsed array - * @throws IllegalArgumentException Parsing exception, or if any of the - * parsed array values is null + * @throws IllegalArgumentException Parsing exception, or if any of the parsed + * array values is null */ public static Object parseArrayAsPrimitiveArray(Class arrayTypeClass, String input) throws IllegalArgumentException { T[] array = parseArray(arrayTypeClass, input); diff --git a/store/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java b/store/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java index 1cc40ef0..c8761bc3 100644 --- a/store/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java @@ -179,8 +179,8 @@ private void ensureVectorSize(ElementImpl element) { if (bitVector == null) { bitVector = new BitVector(sid + 1); } else if (sid >= bitVector.size()) { - int newSize = Math - .min(Math.max(sid + 1, (int) (sid * GraphStoreConfiguration.COLUMNDIFF_GROWING_FACTOR)), Integer.MAX_VALUE); + int newSize = Math.min(Math + .max(sid + 1, (int) (sid * GraphStoreConfiguration.COLUMNDIFF_GROWING_FACTOR)), Integer.MAX_VALUE); bitVector = growBitVector(bitVector, newSize); } } diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java b/store/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java index fe63df6d..0e973312 100644 --- a/store/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java +++ b/store/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java @@ -219,10 +219,10 @@ private void checkType(final Object o) { if (o != null) { Class cl = o.getClass(); if (!(cl.equals(Integer.class) || cl.equals(String.class) || cl.equals(Float.class) || cl - .equals(Double.class) || cl.equals(Short.class) || cl.equals(Byte.class) || cl.equals(Long.class) || cl - .equals(Character.class) || cl.equals(Boolean.class))) { - throw new IllegalArgumentException( - "The type id is " + cl.getCanonicalName() + " but must be a primitive type (int, string, long...)"); + .equals(Double.class) || cl.equals(Short.class) || cl.equals(Byte.class) || cl + .equals(Long.class) || cl.equals(Character.class) || cl.equals(Boolean.class))) { + throw new IllegalArgumentException("The type id is " + cl + .getCanonicalName() + " but must be a primitive type (int, string, long...)"); } if (!configuration.getEdgeLabelType().equals(o.getClass())) { throw new IllegalArgumentException("The expected type was " + configuration.getEdgeLabelType() diff --git a/store/src/main/java/org/gephi/graph/impl/ElementImpl.java b/store/src/main/java/org/gephi/graph/impl/ElementImpl.java index 187dc910..b378a6ab 100644 --- a/store/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -346,7 +346,8 @@ public void setAttribute(Column column, Object value) { if (value != null) { timeIndexStore.add((TimeMap) value); } - } else if (TimeSet.class.isAssignableFrom(column.getTypeClass()) && column.getIndex() == GraphStoreConfiguration.ELEMENT_TIMESET_INDEX) { + } else if (TimeSet.class.isAssignableFrom(column.getTypeClass()) && column + .getIndex() == GraphStoreConfiguration.ELEMENT_TIMESET_INDEX) { if (oldValue != null) { timeIndexStore.remove((TimeSet) oldValue); } @@ -680,7 +681,8 @@ protected GraphStore getGraphStore() { protected void checkTimeRepresentationTimestamp() { if (!getTimeRepresentation().equals(TimeRepresentation.TIMESTAMP)) { - throw new RuntimeException("Can't use timestamps as the configuration is set to " + getTimeRepresentation()); + throw new RuntimeException( + "Can't use timestamps as the configuration is set to " + getTimeRepresentation()); } } @@ -736,28 +738,30 @@ void checkType(Column column, Object value) { if (value != null) { Class typeClass = column.getTypeClass(); if (TimestampMap.class.isAssignableFrom(typeClass)) { - if ((value instanceof Double && (!typeClass.equals(TimestampDoubleMap.class))) || (value instanceof Float && !typeClass - .equals(TimestampFloatMap.class)) || (value instanceof Boolean && !typeClass - .equals(TimestampBooleanMap.class)) || (value instanceof Integer && !typeClass - .equals(TimestampIntegerMap.class)) || (value instanceof Long && !typeClass - .equals(TimestampLongMap.class)) || (value instanceof Short && !typeClass - .equals(TimestampShortMap.class)) || (value instanceof Byte && !typeClass - .equals(TimestampByteMap.class)) || (value instanceof String && !typeClass - .equals(TimestampStringMap.class)) || (value instanceof Character && !typeClass - .equals(TimestampCharMap.class))) { + if ((value instanceof Double && (!typeClass + .equals(TimestampDoubleMap.class))) || (value instanceof Float && !typeClass + .equals(TimestampFloatMap.class)) || (value instanceof Boolean && !typeClass + .equals(TimestampBooleanMap.class)) || (value instanceof Integer && !typeClass + .equals(TimestampIntegerMap.class)) || (value instanceof Long && !typeClass + .equals(TimestampLongMap.class)) || (value instanceof Short && !typeClass + .equals(TimestampShortMap.class)) || (value instanceof Byte && !typeClass + .equals(TimestampByteMap.class)) || (value instanceof String && !typeClass + .equals(TimestampStringMap.class)) || (value instanceof Character && !typeClass + .equals(TimestampCharMap.class))) { throw new IllegalArgumentException( "The object class does not match with the dynamic type (" + typeClass.getName() + ")"); } } else if (IntervalMap.class.isAssignableFrom(typeClass)) { - if ((value instanceof Double && (!typeClass.equals(IntervalDoubleMap.class))) || (value instanceof Float && !typeClass - .equals(IntervalFloatMap.class)) || (value instanceof Boolean && !typeClass - .equals(IntervalBooleanMap.class)) || (value instanceof Integer && !typeClass - .equals(IntervalIntegerMap.class)) || (value instanceof Long && !typeClass - .equals(IntervalLongMap.class)) || (value instanceof Short && !typeClass - .equals(IntervalShortMap.class)) || (value instanceof Byte && !typeClass - .equals(IntervalByteMap.class)) || (value instanceof String && !typeClass - .equals(IntervalStringMap.class)) || (value instanceof Character && !typeClass - .equals(IntervalCharMap.class))) { + if ((value instanceof Double && (!typeClass + .equals(IntervalDoubleMap.class))) || (value instanceof Float && !typeClass + .equals(IntervalFloatMap.class)) || (value instanceof Boolean && !typeClass + .equals(IntervalBooleanMap.class)) || (value instanceof Integer && !typeClass + .equals(IntervalIntegerMap.class)) || (value instanceof Long && !typeClass + .equals(IntervalLongMap.class)) || (value instanceof Short && !typeClass + .equals(IntervalShortMap.class)) || (value instanceof Byte && !typeClass + .equals(IntervalByteMap.class)) || (value instanceof String && !typeClass + .equals(IntervalStringMap.class)) || (value instanceof Character && !typeClass + .equals(IntervalCharMap.class))) { throw new IllegalArgumentException( "The object class does not match with the dynamic type (" + typeClass.getName() + ")"); } diff --git a/store/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/store/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java index c3f92bf1..21b7b151 100644 --- a/store/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java +++ b/store/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java @@ -125,8 +125,8 @@ protected static String parseLiteral(StringReader reader, char quote) throws IOE } /** - * Parses a value until end is detected either by a comma or a bounds - * closing character. + * Parses a value until end is detected either by a comma or a bounds closing + * character. * * @param reader Input reader * @return Parsed value @@ -154,9 +154,9 @@ protected static String parseValue(StringReader reader) throws IOException { } /** - * Converts a string parsed with {@link #parseValue(java.io.StringReader)} - * to the target type, taking into account dynamic parsing quirks such as - * numbers with/without decimals and infinity values. + * Converts a string parsed with {@link #parseValue(java.io.StringReader)} to + * the target type, taking into account dynamic parsing quirks such as numbers + * with/without decimals and infinity values. * * @param Target type * @param typeClass Target type class @@ -167,10 +167,10 @@ protected static T convertValue(Class typeClass, String valString) { Object value; if (typeClass.equals(Byte.class) || typeClass.equals(byte.class) || typeClass.equals(Short.class) || typeClass .equals(short.class) || typeClass.equals(Integer.class) || typeClass.equals(int.class) || typeClass - .equals(Long.class) || typeClass.equals(long.class) || typeClass.equals(BigInteger.class)) { + .equals(Long.class) || typeClass.equals(long.class) || typeClass.equals(BigInteger.class)) { value = parseNumberWithoutDecimals((Class) typeClass, valString); - } else if (typeClass.equals(Float.class) || typeClass.equals(float.class) || typeClass.equals(Double.class) || typeClass - .equals(double.class) || typeClass.equals(BigDecimal.class)) { + } else if (typeClass.equals(Float.class) || typeClass.equals(float.class) || typeClass + .equals(Double.class) || typeClass.equals(double.class) || typeClass.equals(BigDecimal.class)) { value = parseNumberWithDecimals((Class) typeClass, valString); } else { value = AttributeUtils.parse(valString, typeClass); @@ -184,8 +184,7 @@ protected static T convertValue(Class typeClass, String valString) { } /** - * Method for allowing inputs such as "infinity" when parsing decimal - * numbers + * Method for allowing inputs such as "infinity" when parsing decimal numbers * * @param value Input String * @return Input String with fixed "Infinity" syntax if necessary. @@ -215,9 +214,9 @@ private static T parseNumberWithDecimals(Class typeClass, } /** - * Removes anything after the dot of decimal numbers in a string when - * necessary. Used for trying to parse decimal numbers as not decimal. For - * example BigDecimal to BigInteger. + * Removes anything after the dot of decimal numbers in a string when necessary. + * Used for trying to parse decimal numbers as not decimal. For example + * BigDecimal to BigInteger. * * @param s String to remove decimal digits * @return String without dot and decimal digits. @@ -288,8 +287,8 @@ public static String printArray(Object arr) { /** * @param value String value - * @return True if the string contains special characters for arrays - * intervals syntax + * @return True if the string contains special characters for arrays intervals + * syntax */ private static boolean containsArraySpecialCharacters(String value) { for (char c : ARRAY_SPECIAL_CHARACTERS) { diff --git a/store/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java index a98fcc55..c0447508 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java @@ -100,9 +100,8 @@ private void setValueInternal(String key, Object value, Object timeObj) { valueSet = (TimeMap) attributes.get(key); if (!value.getClass().equals(valueSet.getTypeClass())) { - throw new IllegalArgumentException( - "The value type " + value.getClass().getName() + " doesn't match with the expected type " + valueSet - .getTypeClass().getName()); + throw new IllegalArgumentException("The value type " + value.getClass() + .getName() + " doesn't match with the expected type " + valueSet.getTypeClass().getName()); } } else { if (timeObj instanceof Interval) { diff --git a/store/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java index c2d677ba..c57649b1 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java @@ -37,10 +37,10 @@ protected enum AssignConfiguration { public GraphFactoryImpl(GraphStore store) { this.store = store; - this.nodeAssignConfiguration = getAssignConfiguration(AttributeUtils.getStandardizedType(store.configuration - .getNodeIdType())); - this.edgeAssignConfiguration = getAssignConfiguration(AttributeUtils.getStandardizedType(store.configuration - .getEdgeIdType())); + this.nodeAssignConfiguration = getAssignConfiguration(AttributeUtils + .getStandardizedType(store.configuration.getNodeIdType())); + this.edgeAssignConfiguration = getAssignConfiguration(AttributeUtils + .getStandardizedType(store.configuration.getEdgeIdType())); } @Override @@ -208,10 +208,10 @@ public boolean deepEquals(GraphFactoryImpl obj) { } public void resetConfiguration() { - this.nodeAssignConfiguration = getAssignConfiguration(AttributeUtils.getStandardizedType(store.configuration - .getNodeIdType())); - this.edgeAssignConfiguration = getAssignConfiguration(AttributeUtils.getStandardizedType(store.configuration - .getEdgeIdType())); + this.nodeAssignConfiguration = getAssignConfiguration(AttributeUtils + .getStandardizedType(store.configuration.getNodeIdType())); + this.edgeAssignConfiguration = getAssignConfiguration(AttributeUtils + .getStandardizedType(store.configuration.getEdgeIdType())); } protected final AssignConfiguration getAssignConfiguration(Class type) { diff --git a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index bf267070..0a0e77dd 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -427,8 +427,10 @@ public void setConfiguration(Configuration config) { store.autoWriteLock(); try { - if (store.getNodeCount() > 0 || !store.attributes.isEmpty() || store.nodeTable.countColumns() != GraphStoreConfiguration.NODE_DEFAULT_COLUMNS || store.edgeTable - .countColumns() != GraphStoreConfiguration.EDGE_DEFAULT_COLUMNS || store.edgeTypeStore.size() > 1) { + if (store.getNodeCount() > 0 || !store.attributes.isEmpty() || store.nodeTable + .countColumns() != GraphStoreConfiguration.NODE_DEFAULT_COLUMNS || store.edgeTable + .countColumns() != GraphStoreConfiguration.EDGE_DEFAULT_COLUMNS || store.edgeTypeStore + .size() > 1) { throw new IllegalStateException("The store should be empty when modifying the configuration"); } @@ -460,19 +462,19 @@ public void setConfiguration(Configuration config) { edgeTable.removeColumn(GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID); if (config.getTimeRepresentation().equals(TimeRepresentation.TIMESTAMP)) { - nodeTable.store.addColumn(new ColumnImpl(nodeTable, - GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, TimestampSet.class, "Timestamp", null, - Origin.PROPERTY, false, false)); - edgeTable.store.addColumn(new ColumnImpl(nodeTable, - GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, TimestampSet.class, "Timestamp", null, - Origin.PROPERTY, false, false)); + nodeTable.store + .addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, + TimestampSet.class, "Timestamp", null, Origin.PROPERTY, false, false)); + edgeTable.store + .addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, + TimestampSet.class, "Timestamp", null, Origin.PROPERTY, false, false)); } else { - nodeTable.store.addColumn(new ColumnImpl(nodeTable, - GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, IntervalSet.class, "Interval", null, - Origin.PROPERTY, false, false)); - edgeTable.store.addColumn(new ColumnImpl(nodeTable, - GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, IntervalSet.class, "Interval", null, - Origin.PROPERTY, false, false)); + nodeTable.store + .addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, + IntervalSet.class, "Interval", null, Origin.PROPERTY, false, false)); + edgeTable.store + .addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, + IntervalSet.class, "Interval", null, Origin.PROPERTY, false, false)); } configuration.setTimeRepresentation(config.getTimeRepresentation()); store.timeStore.resetConfiguration(); @@ -484,14 +486,14 @@ public void setConfiguration(Configuration config) { if (!config.getEdgeWeightColumn().equals(configuration.getEdgeWeightColumn())) { TableImpl edgeTable = store.edgeTable; if (config.getEdgeWeightColumn()) { - edgeTable.store.garbageQueue.add(edgeTable.store - .intToShort(GraphStoreConfiguration.EDGE_WEIGHT_INDEX)); + edgeTable.store.garbageQueue + .add(edgeTable.store.intToShort(GraphStoreConfiguration.EDGE_WEIGHT_INDEX)); edgeTable.store.addColumn(new ColumnImpl(edgeTable, GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID, config.getEdgeWeightType(), "Weight", null, Origin.PROPERTY, edgeWeightIndexed, false)); } else { edgeTable.removeColumn(GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID); - edgeTable.store.garbageQueue.remove(edgeTable.store - .intToShort(GraphStoreConfiguration.EDGE_WEIGHT_INDEX)); + edgeTable.store.garbageQueue + .remove(edgeTable.store.intToShort(GraphStoreConfiguration.EDGE_WEIGHT_INDEX)); } } diff --git a/store/src/main/java/org/gephi/graph/impl/GraphStore.java b/store/src/main/java/org/gephi/graph/impl/GraphStore.java index 5e1cd885..fd219076 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -86,8 +86,9 @@ public GraphStore(GraphModelImpl model) { version = GraphStoreConfiguration.ENABLE_OBSERVERS ? new GraphVersion(this) : null; observers = GraphStoreConfiguration.ENABLE_OBSERVERS ? new ArrayList<>() : null; spatialIndex = GraphStoreConfiguration.ENABLE_SPATIAL_INDEX ? new SpatialIndexImpl(this) : null; - edgeStore = new EdgeStore(edgeTypeStore, spatialIndex, GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? lock - : null, viewStore, GraphStoreConfiguration.ENABLE_OBSERVERS ? version : null); + edgeStore = new EdgeStore(edgeTypeStore, spatialIndex, + GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? lock : null, viewStore, + GraphStoreConfiguration.ENABLE_OBSERVERS ? version : null); nodeStore = new NodeStore(edgeStore, spatialIndex, GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? lock : null, viewStore, GraphStoreConfiguration.ENABLE_OBSERVERS ? version : null); nodeTable = new TableImpl<>(this, Node.class, GraphStoreConfiguration.ENABLE_INDEX_NODES); @@ -102,10 +103,10 @@ public GraphStore(GraphModelImpl model) { undirectedDecorator = new UndirectedDecorator(this); // Default cols - nodeTable.store.addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID, configuration - .getNodeIdType(), "Id", null, Origin.PROPERTY, false, true)); - edgeTable.store.addColumn(new ColumnImpl(edgeTable, GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID, configuration - .getEdgeIdType(), "Id", null, Origin.PROPERTY, false, true)); + nodeTable.store.addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID, + configuration.getNodeIdType(), "Id", null, Origin.PROPERTY, false, true)); + edgeTable.store.addColumn(new ColumnImpl(edgeTable, GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID, + configuration.getEdgeIdType(), "Id", null, Origin.PROPERTY, false, true)); if (GraphStoreConfiguration.ENABLE_ELEMENT_LABEL) { nodeTable.store.addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_LABEL_COLUMN_ID, String.class, "Label", null, Origin.PROPERTY, false, false)); @@ -785,13 +786,13 @@ protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator } protected EdgeIterableWrapper getEdgeIterableWrapper(Iterator edgeIterator, boolean blocking) { - return new EdgeIterableWrapper(edgeIterator, (blocking && GraphStoreConfiguration.ENABLE_AUTO_LOCKING) ? lock - : null); + return new EdgeIterableWrapper(edgeIterator, + (blocking && GraphStoreConfiguration.ENABLE_AUTO_LOCKING) ? lock : null); } protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator, boolean blocking) { - return new NodeIterableWrapper(nodeIterator, (blocking && GraphStoreConfiguration.ENABLE_AUTO_LOCKING) ? lock - : null); + return new NodeIterableWrapper(nodeIterator, + (blocking && GraphStoreConfiguration.ENABLE_AUTO_LOCKING) ? lock : null); } public int deepHashCode() { diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index ed1391a1..b17cd8a0 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -61,8 +61,8 @@ public Edge getEdge(Node node1, Node node2) { @Override public EdgeIterable getEdges(Node node1, Node node2) { - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore - .getAll(node1, node2, undirected))); + return graphStore + .getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.getAll(node1, node2, undirected))); } @Override @@ -81,8 +81,8 @@ public Edge getEdge(Node node1, Node node2, int type) { @Override public EdgeIterable getEdges(Node node1, Node node2, int type) { - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore - .getAll(node1, node2, type, undirected))); + return graphStore.getEdgeIterableWrapper(new EdgeViewIterator( + graphStore.edgeStore.getAll(node1, node2, type, undirected))); } @Override @@ -102,29 +102,29 @@ public Edge getMutualEdge(Edge e) { @Override public NodeIterable getPredecessors(Node node) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( - graphStore.edgeStore.edgeInIterator(node)))); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, + new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node)))); } @Override public NodeIterable getPredecessors(Node node, int type) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( - graphStore.edgeStore.edgeInIterator(node, type)))); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, + new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node, type)))); } @Override public NodeIterable getSuccessors(Node node) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( - graphStore.edgeStore.edgeOutIterator(node)))); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, + new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node)))); } @Override public NodeIterable getSuccessors(Node node, int type) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new EdgeViewIterator( - graphStore.edgeStore.edgeOutIterator(node, type)))); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, + new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node, type)))); } @Override @@ -345,23 +345,23 @@ public EdgeIterable getSelfLoops() { @Override public NodeIterable getNeighbors(Node node) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new UndirectedEdgeViewIterator( - graphStore.edgeStore.edgeIterator(node)))); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, + new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node)))); } @Override public NodeIterable getNeighbors(Node node, int type) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, new UndirectedEdgeViewIterator( - graphStore.edgeStore.edgeIterator(node, type)))); + return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, + new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node, type)))); } @Override public EdgeIterable getEdges(Node node) { checkValidInViewNodeObject(node); if (undirected) { - return graphStore.getEdgeIterableWrapper(new UndirectedEdgeViewIterator(graphStore.edgeStore - .edgeIterator(node))); + return graphStore + .getEdgeIterableWrapper(new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node))); } else { return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.edgeIterator(node))); } @@ -371,8 +371,8 @@ public EdgeIterable getEdges(Node node) { public EdgeIterable getEdges(Node node, int type) { checkValidInViewNodeObject(node); if (undirected) { - return graphStore.getEdgeIterableWrapper(new UndirectedEdgeViewIterator(graphStore.edgeStore - .edgeIterator(node, type))); + return graphStore.getEdgeIterableWrapper(new UndirectedEdgeViewIterator( + graphStore.edgeStore.edgeIterator(node, type))); } else { return graphStore .getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.edgeIterator(node, type))); diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index 24dcc42a..9670e321 100644 --- a/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -494,8 +494,8 @@ public void not() { if (nodeView) { for (Edge e : graphStore.edgeStore) { boolean t = edgeBitVector.get(e.getStoreId()); - if (t && (!nodeBitVector.get(e.getSource().getStoreId()) || !nodeBitVector.get(e.getTarget() - .getStoreId()))) { + if (t && (!nodeBitVector.get(e.getSource().getStoreId()) || !nodeBitVector + .get(e.getTarget().getStoreId()))) { removeEdge((EdgeImpl) e); } } @@ -628,8 +628,8 @@ protected void destroyAllObservers() { protected void ensureNodeVectorSize(NodeImpl node) { int sid = node.storeId; if (sid >= nodeBitVector.size()) { - int newSize = Math - .min(Math.max(sid + 1, (int) (sid * GraphStoreConfiguration.VIEW_GROWING_FACTOR)), Integer.MAX_VALUE); + int newSize = Math.min(Math + .max(sid + 1, (int) (sid * GraphStoreConfiguration.VIEW_GROWING_FACTOR)), Integer.MAX_VALUE); nodeBitVector = growBitVector(nodeBitVector, newSize); } } @@ -649,8 +649,8 @@ private void ensureEdgeVectorSize(int size) { protected void ensureEdgeVectorSize(EdgeImpl edge) { int sid = edge.storeId; if (sid >= edgeBitVector.size()) { - int newSize = Math - .min(Math.max(sid + 1, (int) (sid * GraphStoreConfiguration.VIEW_GROWING_FACTOR)), Integer.MAX_VALUE); + int newSize = Math.min(Math + .max(sid + 1, (int) (sid * GraphStoreConfiguration.VIEW_GROWING_FACTOR)), Integer.MAX_VALUE); edgeBitVector = growBitVector(edgeBitVector, newSize); } } diff --git a/store/src/main/java/org/gephi/graph/impl/IndexImpl.java b/store/src/main/java/org/gephi/graph/impl/IndexImpl.java index 1f948abb..06884763 100644 --- a/store/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -605,8 +605,8 @@ public Object[] toArray(Object[] array) { if (hasNull()) { if (array.length < size()) { - array = (K[]) java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), map - .size() + 1); + array = (K[]) java.lang.reflect.Array + .newInstance(array.getClass().getComponentType(), map.size() + 1); } array[0] = null; System.arraycopy(map.keySet().toArray(), 0, array, 1, map.size()); diff --git a/store/src/main/java/org/gephi/graph/impl/IndexStore.java b/store/src/main/java/org/gephi/graph/impl/IndexStore.java index 813bf7a9..8bb0c28b 100644 --- a/store/src/main/java/org/gephi/graph/impl/IndexStore.java +++ b/store/src/main/java/org/gephi/graph/impl/IndexStore.java @@ -114,8 +114,8 @@ public Object set(Column column, Object oldValue, Object value, T element) { for (Entry> entry : viewIndexes.entrySet()) { GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); DirectedSubgraph graph = graphView.getDirectedGraph(); - boolean inView = element instanceof Node ? graph.contains((Node) element) : graph - .contains((Edge) element); + boolean inView = element instanceof Node ? graph.contains((Node) element) + : graph.contains((Edge) element); if (inView) { entry.getValue().set(column, oldValue, value, element); } @@ -143,8 +143,8 @@ public void clear(T element) { for (Entry> entry : viewIndexes.entrySet()) { GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); DirectedSubgraph graph = graphView.getDirectedGraph(); - boolean inView = element instanceof Node ? graph.contains((Node) element) : graph - .contains((Edge) element); + boolean inView = element instanceof Node ? graph.contains((Node) element) + : graph.contains((Edge) element); if (inView) { entry.getValue().remove(c, value, element); } diff --git a/store/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java b/store/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java index aeca01c6..cde9af3e 100644 --- a/store/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java +++ b/store/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java @@ -410,8 +410,8 @@ private Interval treeMinimum(Node x) { /** * Returns the interval with the highest right endpoint. * - * @return the interval with the highest right endpoint or null if the tree - * is empty. + * @return the interval with the highest right endpoint or null if the tree is + * empty. */ public Interval maximum() { if (root.left == nil) { @@ -430,8 +430,8 @@ private Node treeMaximum(Node x) { } /** - * Returns the leftmost point or {@code Double.NEGATIVE_INFINITY} in case of - * no intervals. + * Returns the leftmost point or {@code Double.NEGATIVE_INFINITY} in case of no + * intervals. * * @return the leftmost point */ @@ -447,8 +447,8 @@ public double getLow() { } /** - * Returns the rightmost point or {@code Double.POSITIVE_INFINITY} in case - * of no intervals. + * Returns the rightmost point or {@code Double.POSITIVE_INFINITY} in case of no + * intervals. * * @return the rightmost point */ @@ -655,8 +655,7 @@ private void inorderTreeWalk(Node x, List list) { * Compares this interval tree with the specified object for equality. * *

- * Note that two interval trees are equal if they contain the same - * intervals. + * Note that two interval trees are equal if they contain the same intervals. * * @param obj object to which this interval tree is to be compared * diff --git a/store/src/main/java/org/gephi/graph/impl/IntervalsParser.java b/store/src/main/java/org/gephi/graph/impl/IntervalsParser.java index fe28bd58..9de3b4ae 100644 --- a/store/src/main/java/org/gephi/graph/impl/IntervalsParser.java +++ b/store/src/main/java/org/gephi/graph/impl/IntervalsParser.java @@ -125,8 +125,8 @@ public static IntervalSet parseIntervalSet(String input, DateTimeZone timeZone) } /** - * Parses a {@link IntervalSet} type with one or more intervals. Default - * time zone is used (UTC). + * Parses a {@link IntervalSet} type with one or more intervals. Default time + * zone is used (UTC). * * @param input Input string to parse * @return Resulting {@link IntervalSet}, or null if the input equals @@ -144,16 +144,16 @@ public static IntervalSet parseIntervalSet(String input) throws IllegalArgumentE * associated values. * * @param Underlying type of the {@link IntervalMap} values - * @param typeClass Simple type or {@link IntervalMap} subtype for the - * result intervals' values. + * @param typeClass Simple type or {@link IntervalMap} subtype for the result + * intervals' values. * @param input Input string to parse * @param timeZone Time zone to use or null to use default time zone (UTC) * @return Resulting {@link IntervalMap}, or null if the input equals * '<empty>' or is null - * @throws IllegalArgumentException Thrown if type class is not supported, - * any of the intervals don't have a value or have an invalid value, - * there are no intervals in the input string or bounds cannot be - * parsed into doubles or dates/datetimes. + * @throws IllegalArgumentException Thrown if type class is not supported, any + * of the intervals don't have a value or have an invalid value, there + * are no intervals in the input string or bounds cannot be parsed into + * doubles or dates/datetimes. */ public static IntervalMap parseIntervalMap(Class typeClass, String input, DateTimeZone timeZone) throws IllegalArgumentException { if (typeClass == null) { @@ -212,27 +212,27 @@ public static IntervalMap parseIntervalMap(Class typeClass, String inp * associated values. Default time zone is used (UTC). * * @param Underlying type of the {@link IntervalMap} values - * @param typeClass Simple type or {@link IntervalMap} subtype for the - * result intervals' values. + * @param typeClass Simple type or {@link IntervalMap} subtype for the result + * intervals' values. * @param input Input string to parse * @return Resulting {@link IntervalMap}, or null if the input equals * '<empty>' or is null - * @throws IllegalArgumentException Thrown if type class is not supported, - * any of the intervals don't have a value or have an invalid value, - * there are no intervals in the input string or bounds cannot be - * parsed into doubles or dates/datetimes. + * @throws IllegalArgumentException Thrown if type class is not supported, any + * of the intervals don't have a value or have an invalid value, there + * are no intervals in the input string or bounds cannot be parsed into + * doubles or dates/datetimes. */ public static IntervalMap parseIntervalMap(Class typeClass, String input) throws IllegalArgumentException { return parseIntervalMap(typeClass, input, null); } /** - * Parses intervals with values (of {@code typeClass} Class) or without - * values (null {@code typeClass} Class) + * Parses intervals with values (of {@code typeClass} Class) or without values + * (null {@code typeClass} Class) * * @param Type of the interval value - * @param typeClass Class of the intervals' values or null to parse - * intervals without values + * @param typeClass Class of the intervals' values or null to parse intervals + * without values * @param input Input to parse * @param timeZone Time zone to use or null to use default time zone (UTC) * @return List of Interval diff --git a/store/src/main/java/org/gephi/graph/impl/NodeStore.java b/store/src/main/java/org/gephi/graph/impl/NodeStore.java index 53a643f7..9b0f3de0 100644 --- a/store/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/store/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -637,7 +637,8 @@ public NodeImpl next() { public void remove() { checkWriteLock(); if (edgeStore != null) { - for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(pointer); edgeIterator.hasNext();) { + for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(pointer); edgeIterator + .hasNext();) { edgeIterator.next(); edgeIterator.remove(); } diff --git a/store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index 34a15a91..f3c58f82 100644 --- a/store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -428,7 +428,8 @@ private void insert(NodeImpl item) { } } - if (objects == null || (childTL == null && (level >= maxLevels || objects.size() + 1 <= maxObjectsPerNode))) { + if (objects == null || (childTL == null && (level >= maxLevels || objects + .size() + 1 <= maxObjectsPerNode))) { // If there's room to add the object, just add it add(item); } else { diff --git a/store/src/main/java/org/gephi/graph/impl/TimeIndexStore.java b/store/src/main/java/org/gephi/graph/impl/TimeIndexStore.java index bb3bebc4..ef5172b9 100644 --- a/store/src/main/java/org/gephi/graph/impl/TimeIndexStore.java +++ b/store/src/main/java/org/gephi/graph/impl/TimeIndexStore.java @@ -388,8 +388,8 @@ private void checkTimeIndex(Integer timeIndex) { protected void ensureArraySize(int index) { if (index >= countMap.length) { - int newSize = Math - .min(Math.max(index + 1, (int) (index * GraphStoreConfiguration.TIMESTAMP_STORE_GROWING_FACTOR)), Integer.MAX_VALUE); + int newSize = Math.min(Math + .max(index + 1, (int) (index * GraphStoreConfiguration.TIMESTAMP_STORE_GROWING_FACTOR)), Integer.MAX_VALUE); int[] newArray = new int[newSize]; System.arraycopy(countMap, 0, newArray, 0, countMap.length); countMap = newArray; diff --git a/store/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java b/store/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java index 2392961c..0ac2a2e0 100644 --- a/store/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java +++ b/store/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java @@ -69,8 +69,8 @@ public double getMaxTimestamp() { } else { Double2IntSortedMap sortedMap = (Double2IntSortedMap) timestampIndexStore.timeSortedMap; if (!sortedMap.isEmpty()) { - ObjectBidirectionalIterator bi = sortedMap.double2IntEntrySet().iterator(sortedMap - .double2IntEntrySet().last()); + ObjectBidirectionalIterator bi = sortedMap.double2IntEntrySet() + .iterator(sortedMap.double2IntEntrySet().last()); while (bi.hasPrevious()) { Double2IntMap.Entry entry = bi.previous(); double timestamp = entry.getDoubleKey(); diff --git a/store/src/main/java/org/gephi/graph/impl/TimestampsParser.java b/store/src/main/java/org/gephi/graph/impl/TimestampsParser.java index c21e0605..6d075194 100644 --- a/store/src/main/java/org/gephi/graph/impl/TimestampsParser.java +++ b/store/src/main/java/org/gephi/graph/impl/TimestampsParser.java @@ -159,8 +159,8 @@ public static TimestampSet parseTimestampSet(String input, DateTimeZone timeZone } /** - * Parses a {@link TimestampSet} type with one or more timestamps. Default - * time zone is used (UTC). + * Parses a {@link TimestampSet} type with one or more timestamps. Default time + * zone is used (UTC). * * @param input Input string to parse * @return Resulting {@link TimestampSet}, or null if the input equals @@ -178,16 +178,16 @@ public static TimestampSet parseTimestampSet(String input) throws IllegalArgumen * associated values. * * @param Underlying type of the {@link TimestampMap} values - * @param typeClass Simple type or {@link TimestampMap} subtype for the - * result values. + * @param typeClass Simple type or {@link TimestampMap} subtype for the result + * values. * @param input Input string to parse * @param timeZone Time zone to use or null to use default time zone (UTC) * @return Resulting {@link TimestampMap}, or null if the input equals * '<empty>' or is null - * @throws IllegalArgumentException Thrown if type class is not supported, - * any of the timestamps don't have a value or have an invalid - * value, there are no timestamps in the input string or bounds - * cannot be parsed into doubles or dates/datetimes. + * @throws IllegalArgumentException Thrown if type class is not supported, any + * of the timestamps don't have a value or have an invalid value, there + * are no timestamps in the input string or bounds cannot be parsed into + * doubles or dates/datetimes. */ public static TimestampMap parseTimestampMap(Class typeClass, String input, DateTimeZone timeZone) throws IllegalArgumentException { if (typeClass == null) { @@ -261,15 +261,15 @@ public static TimestampMap parseTimestampMap(Class typeClass, String i * associated values. Default time zone is used (UTC). * * @param Underlying type of the {@link TimestampMap} values - * @param typeClass Simple type or {@link TimestampMap} subtype for the - * result values. + * @param typeClass Simple type or {@link TimestampMap} subtype for the result + * values. * @param input Input string to parse * @return Resulting {@link TimestampMap}, or null if the input equals * '<empty>' or is null - * @throws IllegalArgumentException Thrown if type class is not supported, - * any of the timestamps don't have a value or have an invalid - * value, there are no timestamps in the input string or bounds - * cannot be parsed into doubles or dates/datetimes. + * @throws IllegalArgumentException Thrown if type class is not supported, any + * of the timestamps don't have a value or have an invalid value, there + * are no timestamps in the input string or bounds cannot be parsed into + * doubles or dates/datetimes. */ public static TimestampMap parseTimestampMap(Class typeClass, String input) throws IllegalArgumentException { return parseTimestampMap(typeClass, input, null); diff --git a/store/src/main/java/org/gephi/graph/impl/utils/LongPacker.java b/store/src/main/java/org/gephi/graph/impl/utils/LongPacker.java index 9dab4a4b..cb7eb489 100644 --- a/store/src/main/java/org/gephi/graph/impl/utils/LongPacker.java +++ b/store/src/main/java/org/gephi/graph/impl/utils/LongPacker.java @@ -60,8 +60,8 @@ static public int packLong(DataOutput os, long value) throws IOException { } /** - * Pack non-negative long into byte array. It will occupy 1-10 bytes - * depending on value (lower values occupy smaller space) + * Pack non-negative long into byte array. It will occupy 1-10 bytes depending + * on value (lower values occupy smaller space) * * @param ba the byte array * @param value the long value @@ -136,8 +136,8 @@ static public long unpackLong(byte[] ba, int index) { } /** - * Pack non-negative int into output stream. It will occupy 1-5 bytes - * depending on value (lower values occupy smaller space) + * Pack non-negative int into output stream. It will occupy 1-5 bytes depending + * on value (lower values occupy smaller space) * * @param os the data output * @param value the value diff --git a/store/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java b/store/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java index 182fd2ce..642e2deb 100644 --- a/store/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java +++ b/store/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java @@ -28,8 +28,8 @@ private MapDeepEquals() { /** * Compares two maps for equality. This is based around the idea that if the - * keys are deep equal and the values the keys return are deep equal then - * the maps are equal. + * keys are deep equal and the values the keys return are deep equal then the + * maps are equal. * * @param m1 - first map * @param m2 - second map diff --git a/store/src/main/java/org/gephi/graph/spi/LayoutData.java b/store/src/main/java/org/gephi/graph/spi/LayoutData.java index 0685ec14..91e54ce7 100644 --- a/store/src/main/java/org/gephi/graph/spi/LayoutData.java +++ b/store/src/main/java/org/gephi/graph/spi/LayoutData.java @@ -22,8 +22,7 @@ * efficiently. *

* Layout implementations can implement this interface and use the - * {@link Node#setLayoutData(org.gephi.graph.spi.LayoutData) - * } method to + * {@link Node#setLayoutData(org.gephi.graph.spi.LayoutData) } method to * associate any metadata with the node. */ public interface LayoutData { diff --git a/store/src/main/java/org/gephi/graph/spi/package.html b/store/src/main/java/org/gephi/graph/spi/package.html index 1c426205..be31cfec 100644 --- a/store/src/main/java/org/gephi/graph/spi/package.html +++ b/store/src/main/java/org/gephi/graph/spi/package.html @@ -1,3 +1,6 @@ - - SPI interfaces clients can implement to extend the API. - + + + + SPI interfaces clients can implement to extend the API. + + \ No newline at end of file diff --git a/store/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java b/store/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java index 338cc2f2..f1a2bf8c 100644 --- a/store/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java +++ b/store/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java @@ -89,8 +89,8 @@ public void testMultiplePutWithOverlap() { Assert.assertTrue(set.put(new Interval(2.0, 2.0), defaultValues[0])); Assert.assertTrue(set.put(new Interval(2.0, 3.0), defaultValues[1])); defaultValues = new Object[] { defaultValues[0], defaultValues[0], defaultValues[1], defaultValues[1] }; - testValues(set, new Interval[] { new Interval(1.0, 2.0), new Interval(2.0, 2.0), new Interval(2.0, 3.0), new Interval( - 3.0, 4.0) }, defaultValues); + testValues(set, new Interval[] { new Interval(1.0, 2.0), new Interval(2.0, 2.0), new Interval(2.0, + 3.0), new Interval(3.0, 4.0) }, defaultValues); } } @@ -226,14 +226,22 @@ public void getIntervalsWeight() { j.put(new Interval(2003, 2004), 42); j.put(new Interval(2005, 2006), 42); - Assert.assertEquals(j.getIntervalsWeight(2000, 2002, j.getOverlappingIntervals(2000, 2002)), new double[] { 2.0 }); - Assert.assertEquals(j.getIntervalsWeight(2001, 2002, j.getOverlappingIntervals(2001, 2002)), new double[] { 1.0 }); - Assert.assertEquals(j.getIntervalsWeight(2000, 2001, j.getOverlappingIntervals(2000, 2001)), new double[] { 1.0 }); - Assert.assertEquals(j.getIntervalsWeight(2000.5, 2001.5, j.getOverlappingIntervals(2000.5, 2001.5)), new double[] { 1.0 }); - Assert.assertEquals(j.getIntervalsWeight(1999, 2000, j.getOverlappingIntervals(1999, 2000)), new double[] { 0 }); - Assert.assertEquals(j.getIntervalsWeight(1999, 2001, j.getOverlappingIntervals(1999, 2001)), new double[] { 1.0 }); - Assert.assertEquals(j.getIntervalsWeight(2000, 2003, j.getOverlappingIntervals(2000, 2003)), new double[] { 2.0, 0 }); - Assert.assertEquals(j.getIntervalsWeight(2000, 2004, j.getOverlappingIntervals(2000, 2004)), new double[] { 2.0, 1.0 }); + Assert.assertEquals(j + .getIntervalsWeight(2000, 2002, j.getOverlappingIntervals(2000, 2002)), new double[] { 2.0 }); + Assert.assertEquals(j + .getIntervalsWeight(2001, 2002, j.getOverlappingIntervals(2001, 2002)), new double[] { 1.0 }); + Assert.assertEquals(j + .getIntervalsWeight(2000, 2001, j.getOverlappingIntervals(2000, 2001)), new double[] { 1.0 }); + Assert.assertEquals(j + .getIntervalsWeight(2000.5, 2001.5, j.getOverlappingIntervals(2000.5, 2001.5)), new double[] { 1.0 }); + Assert.assertEquals(j + .getIntervalsWeight(1999, 2000, j.getOverlappingIntervals(1999, 2000)), new double[] { 0 }); + Assert.assertEquals(j + .getIntervalsWeight(1999, 2001, j.getOverlappingIntervals(1999, 2001)), new double[] { 1.0 }); + Assert.assertEquals(j + .getIntervalsWeight(2000, 2003, j.getOverlappingIntervals(2000, 2003)), new double[] { 2.0, 0 }); + Assert.assertEquals(j + .getIntervalsWeight(2000, 2004, j.getOverlappingIntervals(2000, 2004)), new double[] { 2.0, 1.0 }); } @Test @@ -406,8 +414,8 @@ public void testShortEstimators() { @Test public void testEquals() { - Interval[] indices = new Interval[] { new Interval(1.0, 2.0), new Interval(3.0, 4.0), new Interval(2.0, 2.0), new Interval( - 2.0, 3.0) }; + Interval[] indices = new Interval[] { new Interval(1.0, 2.0), new Interval(3.0, 4.0), new Interval(2.0, + 2.0), new Interval(2.0, 3.0) }; String[] values = new String[] { "a", "z", "e" }; IntervalStringMap set1 = new IntervalStringMap(); IntervalStringMap set2 = new IntervalStringMap(); @@ -505,7 +513,8 @@ public void testToStringDouble() { Assert.assertEquals(map1.toString(), "<[1.0, 2.0, foo]; [4.0, 5.5, bar]>"); map1.put(new Interval(6.0, 9.0), " 'test' "); - Assert.assertEquals(map1.toString(TimeFormat.DOUBLE), "<[1.0, 2.0, foo]; [4.0, 5.5, bar]; [6.0, 9.0, \" 'test' \"]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DOUBLE), "<[1.0, 2.0, foo]; [4.0, 5.5, bar]; [6.0, 9.0, \" 'test' \"]>"); } @Test @@ -513,18 +522,24 @@ public void testToStringDate() { IntervalStringMap map1 = new IntervalStringMap(); Assert.assertEquals(map1.toString(TimeFormat.DATE), ""); - map1.put(new Interval(AttributeUtils.parseDateTime("2012-02-29"), AttributeUtils.parseDateTime("2012-03-01")), "foo"); + map1.put(new Interval(AttributeUtils.parseDateTime("2012-02-29"), + AttributeUtils.parseDateTime("2012-03-01")), "foo"); Assert.assertEquals(map1.toString(TimeFormat.DATE), "<[2012-02-29, 2012-03-01, foo]>"); - map1.put(new Interval(AttributeUtils.parseDateTime("2012-07-17T00:02:21"), AttributeUtils - .parseDateTime("2012-07-17T00:03:00")), "bar"); - Assert.assertEquals(map1.toString(TimeFormat.DATE), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0, foo]; [1342483341000.0, 1342483380000.0, bar]>"); + map1.put(new Interval(AttributeUtils.parseDateTime("2012-07-17T00:02:21"), + AttributeUtils.parseDateTime("2012-07-17T00:03:00")), "bar"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATE), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0, foo]; [1342483341000.0, 1342483380000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone.forID("+03:00")), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone.forID("-03:00")), "<[2012-02-28, 2012-02-29, foo]; [2012-07-16, 2012-07-16, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone + .forID("+03:00")), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone + .forID("-03:00")), "<[2012-02-28, 2012-02-29, foo]; [2012-07-16, 2012-07-16, bar]>"); // Test infinity: IntervalStringMap mapInf = new IntervalStringMap(); @@ -538,28 +553,37 @@ public void testToStringDatetime() { Assert.assertEquals(map1.toString(TimeFormat.DATETIME), ""); // Test with default timezone UTC+0 - map1.put(new Interval(AttributeUtils.parseDateTime("2012-02-29"), AttributeUtils.parseDateTime("2012-03-01")), "foo"); - Assert.assertEquals(map1.toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]>"); - - map1.put(new Interval(AttributeUtils.parseDateTime("2012-07-17T01:10:44"), AttributeUtils - .parseDateTime("2012-07-17T01:10:45")), "bar"); - Assert.assertEquals(map1.toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0, foo]; [1342487444000.0, 1342487445000.0, bar]>"); + map1.put(new Interval(AttributeUtils.parseDateTime("2012-02-29"), + AttributeUtils.parseDateTime("2012-03-01")), "foo"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]>"); + + map1.put(new Interval(AttributeUtils.parseDateTime("2012-07-17T01:10:44"), + AttributeUtils.parseDateTime("2012-07-17T01:10:45")), "bar"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0, foo]; [1342487444000.0, 1342487445000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, DateTimeZone.forID("+12:30")), "<[2012-02-29T12:30:00.000+12:30, 2012-03-01T12:30:00.000+12:30, foo]; [2012-07-17T13:40:44.000+12:30, 2012-07-17T13:40:45.000+12:30, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, DateTimeZone + .forID("+12:30")), "<[2012-02-29T12:30:00.000+12:30, 2012-03-01T12:30:00.000+12:30, foo]; [2012-07-17T13:40:44.000+12:30, 2012-07-17T13:40:45.000+12:30, bar]>"); // Test with timezone parsing and UTC printing: IntervalStringMap map2 = new IntervalStringMap(); - map2.put(new Interval(AttributeUtils.parseDateTime("2012-02-29T00:00:00+02:30"), AttributeUtils - .parseDateTime("2012-02-29T02:30:00+02:30")), "foo"); - Assert.assertEquals(map2.toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T00:00:00.000Z, foo]>"); - - map2.put(new Interval(AttributeUtils.parseDateTime("2012-02-29T01:10:44+00:00"), AttributeUtils - .parseDateTime("2012-02-29T01:10:45+00:00")), "bar"); - Assert.assertEquals(map2.toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, 2012-02-29T01:10:45.000Z, bar]>"); - Assert.assertEquals(map2.toString(TimeFormat.DOUBLE), "<[1330464600000.0, 1330473600000.0, foo]; [1330477844000.0, 1330477845000.0, bar]>"); + map2.put(new Interval(AttributeUtils.parseDateTime("2012-02-29T00:00:00+02:30"), + AttributeUtils.parseDateTime("2012-02-29T02:30:00+02:30")), "foo"); + Assert.assertEquals(map2 + .toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T00:00:00.000Z, foo]>"); + + map2.put(new Interval(AttributeUtils.parseDateTime("2012-02-29T01:10:44+00:00"), + AttributeUtils.parseDateTime("2012-02-29T01:10:45+00:00")), "bar"); + Assert.assertEquals(map2 + .toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, 2012-02-29T01:10:45.000Z, bar]>"); + Assert.assertEquals(map2 + .toString(TimeFormat.DOUBLE), "<[1330464600000.0, 1330473600000.0, foo]; [1330477844000.0, 1330477845000.0, bar]>"); // Test infinity: IntervalStringMap mapInf = new IntervalStringMap(); @@ -580,9 +604,10 @@ private IntervalMap[] getAllInstances() { } private IntervalMap[] getAllInstances(int capacity) { - return new IntervalMap[] { new IntervalStringMap(capacity), new IntervalBooleanMap(capacity), new IntervalFloatMap( - capacity), new IntervalDoubleMap(capacity), new IntervalIntegerMap(capacity), new IntervalShortMap( - capacity), new IntervalLongMap(capacity), new IntervalByteMap(capacity), new IntervalCharMap(capacity) }; + return new IntervalMap[] { new IntervalStringMap(capacity), new IntervalBooleanMap( + capacity), new IntervalFloatMap(capacity), new IntervalDoubleMap(capacity), new IntervalIntegerMap( + capacity), new IntervalShortMap(capacity), new IntervalLongMap( + capacity), new IntervalByteMap(capacity), new IntervalCharMap(capacity) }; } private Object[] getTestValues(IntervalMap set) { @@ -650,7 +675,8 @@ private void testValues(IntervalMap set, Interval[] expectedIntervals, Object[] .getMethod("get" + typeClass.getSimpleName(), Interval.class, getMethod.getReturnType()); Assert.assertEquals(getMethod.invoke(set, expectedIntervals[i]), expectedValues[i]); - Assert.assertEquals(getMethodWithDefault.invoke(set, expectedIntervals[i], getDefaultValue(set)), expectedValues[i]); + Assert.assertEquals(getMethodWithDefault + .invoke(set, expectedIntervals[i], getDefaultValue(set)), expectedValues[i]); Assert.assertEquals(getMethodWithDefault .invoke(set, new Interval(99999.0, 999999.0), getDefaultValue(set)), getDefaultValue(set)); diff --git a/store/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java b/store/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java index b097ed03..be0e0b6b 100644 --- a/store/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java +++ b/store/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java @@ -333,18 +333,23 @@ public void testToStringDate() { set1.add(new Interval(AttributeUtils.parseDateTime("2012-02-29"), AttributeUtils.parseDateTime("2012-03-01"))); Assert.assertEquals(set1.toString(TimeFormat.DATE), "<[2012-02-29, 2012-03-01]>"); - set1.add(new Interval(AttributeUtils.parseDateTime("2012-07-17T00:02:21"), AttributeUtils - .parseDateTime("2012-07-17T00:03:00"))); + set1.add(new Interval(AttributeUtils.parseDateTime("2012-07-17T00:02:21"), + AttributeUtils.parseDateTime("2012-07-17T00:03:00"))); Assert.assertEquals(set1.toString(TimeFormat.DATE), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); - Assert.assertEquals(set1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0]; [1342483341000.0, 1342483380000.0]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0]; [1342483341000.0, 1342483380000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.forID("+12:00")), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); - set1.add(new Interval(AttributeUtils.parseDateTime("2012-07-18T18:30:00"), AttributeUtils - .parseDateTime("2012-07-18T18:30:01"))); - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.forID("+08:00")), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]; [2012-07-19, 2012-07-19]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.forID("-10:00")), "<[2012-02-28, 2012-02-29]; [2012-07-16, 2012-07-16]; [2012-07-18, 2012-07-18]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone + .forID("+12:00")), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); + set1.add(new Interval(AttributeUtils.parseDateTime("2012-07-18T18:30:00"), + AttributeUtils.parseDateTime("2012-07-18T18:30:01"))); + Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone + .forID("+08:00")), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]; [2012-07-19, 2012-07-19]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone + .forID("-10:00")), "<[2012-02-28, 2012-02-29]; [2012-07-16, 2012-07-16]; [2012-07-18, 2012-07-18]>"); // Test infinity: IntervalSet setInf = new IntervalSet(); @@ -359,27 +364,35 @@ public void testToStringDatetime() { // Test with default timezone UTC+0 set1.add(new Interval(AttributeUtils.parseDateTime("2012-02-29"), AttributeUtils.parseDateTime("2012-03-01"))); - Assert.assertEquals(set1.toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]>"); - set1.add(new Interval(AttributeUtils.parseDateTime("2012-07-17T01:10:44"), AttributeUtils - .parseDateTime("2012-07-17T01:10:45"))); - Assert.assertEquals(set1.toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z]>"); - Assert.assertEquals(set1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0]; [1342487444000.0, 1342487445000.0]>"); + set1.add(new Interval(AttributeUtils.parseDateTime("2012-07-17T01:10:44"), + AttributeUtils.parseDateTime("2012-07-17T01:10:45"))); + Assert.assertEquals(set1 + .toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0]; [1342487444000.0, 1342487445000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, DateTimeZone.forID("+12:00")), "<[2012-02-29T12:00:00.000+12:00, 2012-03-01T12:00:00.000+12:00]; [2012-07-17T13:10:44.000+12:00, 2012-07-17T13:10:45.000+12:00]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, DateTimeZone + .forID("+12:00")), "<[2012-02-29T12:00:00.000+12:00, 2012-03-01T12:00:00.000+12:00]; [2012-07-17T13:10:44.000+12:00, 2012-07-17T13:10:45.000+12:00]>"); // Test with timezone parsing and UTC printing: IntervalSet set2 = new IntervalSet(); - set2.add(new Interval(AttributeUtils.parseDateTime("2012-02-29T00:00:00+02:30"), AttributeUtils - .parseDateTime("2012-02-29T02:30:00+02:30"))); - Assert.assertEquals(set2.toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T00:00:00.000Z]>"); - - set2.add(new Interval(AttributeUtils.parseDateTime("2012-02-29T01:10:44+00:00"), AttributeUtils - .parseDateTime("2012-02-29T01:10:45+00:00"))); - Assert.assertEquals(set2.toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T00:00:00.000Z]; [2012-02-29T01:10:44.000Z, 2012-02-29T01:10:45.000Z]>"); - Assert.assertEquals(set2.toString(TimeFormat.DOUBLE), "<[1330464600000.0, 1330473600000.0]; [1330477844000.0, 1330477845000.0]>"); + set2.add(new Interval(AttributeUtils.parseDateTime("2012-02-29T00:00:00+02:30"), + AttributeUtils.parseDateTime("2012-02-29T02:30:00+02:30"))); + Assert.assertEquals(set2 + .toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T00:00:00.000Z]>"); + + set2.add(new Interval(AttributeUtils.parseDateTime("2012-02-29T01:10:44+00:00"), + AttributeUtils.parseDateTime("2012-02-29T01:10:45+00:00"))); + Assert.assertEquals(set2 + .toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T00:00:00.000Z]; [2012-02-29T01:10:44.000Z, 2012-02-29T01:10:45.000Z]>"); + Assert.assertEquals(set2 + .toString(TimeFormat.DOUBLE), "<[1330464600000.0, 1330473600000.0]; [1330477844000.0, 1330477845000.0]>"); // Test infinity: IntervalSet setInf = new IntervalSet(); diff --git a/store/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java b/store/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java index d0120129..4cb0fcd4 100644 --- a/store/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java +++ b/store/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java @@ -625,7 +625,8 @@ public void testToStringDouble() { map1.put(6.0, " 'test' "); map1.put(9.0, " 'test' "); - Assert.assertEquals(map1.toString(TimeFormat.DOUBLE), "<[1.0, foo]; [5.5, bar]; [6.0, \" 'test' \"]; [9.0, \" 'test' \"]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DOUBLE), "<[1.0, foo]; [5.5, bar]; [6.0, \" 'test' \"]; [9.0, \" 'test' \"]>"); } @Test @@ -642,8 +643,10 @@ public void testToStringDate() { // Test with time zone printing: Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, foo]; [2012-02-29, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone.forID("+03:00")), "<[2012-02-29, foo]; [2012-02-29, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone.forID("-03:00")), "<[2012-02-28, foo]; [2012-02-28, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATE, DateTimeZone.forID("+03:00")), "<[2012-02-29, foo]; [2012-02-29, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATE, DateTimeZone.forID("-03:00")), "<[2012-02-28, foo]; [2012-02-28, bar]>"); // Test infinity: TimestampStringMap mapInf = new TimestampStringMap(); @@ -662,12 +665,15 @@ public void testToStringDatetime() { Assert.assertEquals(map1.toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, foo]>"); map1.put(AttributeUtils.parseDateTime("2012-02-29T01:10:44"), "bar"); - Assert.assertEquals(map1.toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, bar]>"); Assert.assertEquals(map1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, foo]; [1330477844000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, DateTimeZone.forID("-01:30")), "<[2012-02-28T22:30:00.000-01:30, foo]; [2012-02-28T23:40:44.000-01:30, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, DateTimeZone + .forID("-01:30")), "<[2012-02-28T22:30:00.000-01:30, foo]; [2012-02-28T23:40:44.000-01:30, bar]>"); // Test with timezone parsing and UTC printing: TimestampStringMap map2 = new TimestampStringMap(); @@ -675,7 +681,8 @@ public void testToStringDatetime() { Assert.assertEquals(map2.toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, foo]>"); map2.put(AttributeUtils.parseDateTime("2012-02-29T01:10:44-01:00"), "bar"); - Assert.assertEquals(map2.toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, foo]; [2012-02-29T02:10:44.000Z, bar]>"); + Assert.assertEquals(map2 + .toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, foo]; [2012-02-29T02:10:44.000Z, bar]>"); Assert.assertEquals(map2.toString(TimeFormat.DOUBLE), "<[1330464600000.0, foo]; [1330481444000.0, bar]>"); // Test infinity: @@ -698,10 +705,10 @@ private TimestampMap[] getAllInstances() { } private TimestampMap[] getAllInstances(int capacity) { - return new TimestampMap[] { new TimestampDoubleMap(capacity), new TimestampByteMap(capacity), new TimestampFloatMap( - capacity), new TimestampIntegerMap(capacity), new TimestampLongMap(capacity), new TimestampShortMap( - capacity), new TimestampStringMap(capacity), new TimestampCharMap(capacity), new TimestampBooleanMap( - capacity) }; + return new TimestampMap[] { new TimestampDoubleMap(capacity), new TimestampByteMap( + capacity), new TimestampFloatMap(capacity), new TimestampIntegerMap(capacity), new TimestampLongMap( + capacity), new TimestampShortMap(capacity), new TimestampStringMap( + capacity), new TimestampCharMap(capacity), new TimestampBooleanMap(capacity) }; } private Object[] getTestValues(TimestampMap set) { @@ -771,8 +778,10 @@ private void testValues(TimestampMap set, double[] expectedTimestamp, Object[] e .getMethod("get" + typeClass.getSimpleName(), double.class, getMethod.getReturnType()); Assert.assertEquals(getMethod.invoke(set, expectedTimestamp[i]), expectedValues[i]); - Assert.assertEquals(getMethodWithDefault.invoke(set, expectedTimestamp[i], getDefaultValue(set)), expectedValues[i]); - Assert.assertEquals(getMethodWithDefault.invoke(set, 999999.0, getDefaultValue(set)), getDefaultValue(set)); + Assert.assertEquals(getMethodWithDefault + .invoke(set, expectedTimestamp[i], getDefaultValue(set)), expectedValues[i]); + Assert.assertEquals(getMethodWithDefault + .invoke(set, 999999.0, getDefaultValue(set)), getDefaultValue(set)); boolean thrown = false; try { diff --git a/store/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java b/store/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java index 8cd950be..b10a1f35 100644 --- a/store/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java +++ b/store/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java @@ -155,7 +155,8 @@ public void testRemoveAddLoop() { } } - testDoubleArrayEquals(set.toPrimitiveArray(), NumberGenerator.sortAndRemoveDuplicates(doubleSet.toDoubleArray())); + testDoubleArrayEquals(set.toPrimitiveArray(), NumberGenerator + .sortAndRemoveDuplicates(doubleSet.toDoubleArray())); double[] newArray = NumberGenerator.generateRandomDouble(count / 2, true); for (int i = 0; i < count / 2; i++) { @@ -164,7 +165,8 @@ public void testRemoveAddLoop() { doubleSet.add(number); } - testDoubleArrayEquals(set.toPrimitiveArray(), NumberGenerator.sortAndRemoveDuplicates(doubleSet.toDoubleArray())); + testDoubleArrayEquals(set.toPrimitiveArray(), NumberGenerator + .sortAndRemoveDuplicates(doubleSet.toDoubleArray())); } @Test @@ -295,8 +297,10 @@ public void testToStringDate() { Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, 2012-02-29]>"); Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.forID("+12:00")), "<[2012-02-29, 2012-02-29]>"); set1.add(AttributeUtils.parseDateTime("2012-07-18T18:30:00")); - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.forID("+08:00")), "<[2012-02-29, 2012-02-29, 2012-07-19]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.forID("-10:00")), "<[2012-02-28, 2012-02-28, 2012-07-18]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DATE, DateTimeZone.forID("+08:00")), "<[2012-02-29, 2012-02-29, 2012-07-19]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DATE, DateTimeZone.forID("-10:00")), "<[2012-02-28, 2012-02-28, 2012-07-18]>"); // Test infinity: TimestampSet setInf = new TimestampSet(); @@ -315,12 +319,15 @@ public void testToStringDatetime() { Assert.assertEquals(set1.toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z]>"); set1.add(AttributeUtils.parseDateTime("2012-02-29T01:10:44")); - Assert.assertEquals(set1.toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-02-29T01:10:44.000Z]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DATETIME), "<[2012-02-29T00:00:00.000Z, 2012-02-29T01:10:44.000Z]>"); Assert.assertEquals(set1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330477844000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, 2012-02-29T01:10:44.000Z]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, DateTimeZone.forID("+12:15")), "<[2012-02-29T12:15:00.000+12:15, 2012-02-29T13:25:44.000+12:15]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, 2012-02-29T01:10:44.000Z]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, DateTimeZone + .forID("+12:15")), "<[2012-02-29T12:15:00.000+12:15, 2012-02-29T13:25:44.000+12:15]>"); // Test with timezone parsing and UTC printing: TimestampSet set2 = new TimestampSet(); @@ -328,7 +335,8 @@ public void testToStringDatetime() { Assert.assertEquals(set2.toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z]>"); set2.add(AttributeUtils.parseDateTime("2012-02-29T01:10:44-01:00")); - Assert.assertEquals(set2.toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T02:10:44.000Z]>"); + Assert.assertEquals(set2 + .toString(TimeFormat.DATETIME), "<[2012-02-28T21:30:00.000Z, 2012-02-29T02:10:44.000Z]>"); Assert.assertEquals(set2.toString(TimeFormat.DOUBLE), "<[1330464600000.0, 1330481444000.0]>"); // Test infinity: diff --git a/store/src/test/java/org/gephi/graph/impl/ArraysParserTest.java b/store/src/test/java/org/gephi/graph/impl/ArraysParserTest.java index 8fdcf16e..af57c936 100644 --- a/store/src/test/java/org/gephi/graph/impl/ArraysParserTest.java +++ b/store/src/test/java/org/gephi/graph/impl/ArraysParserTest.java @@ -133,8 +133,9 @@ public void testParseBigDecimal() { BigDecimal[] a1 = ArraysParser .parseArray(BigDecimal[].class, "[123456789123456789123456789.123456789123456789123456789, -123456789123456789123456789.123456789123456789123456789]"); - Assert.assertEquals(new BigDecimal[] { new BigDecimal("123456789123456789123456789.123456789123456789123456789"), new BigDecimal( - "-123456789123456789123456789.123456789123456789123456789") }, a1); + Assert.assertEquals(new BigDecimal[] { new BigDecimal( + "123456789123456789123456789.123456789123456789123456789"), new BigDecimal( + "-123456789123456789123456789.123456789123456789123456789") }, a1); } @Test diff --git a/store/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/store/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java index 18f95ca6..11ce8d7f 100644 --- a/store/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java +++ b/store/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java @@ -82,11 +82,12 @@ public void testParseSimpleTypes() { Assert.assertEquals(AttributeUtils.parse("true", Boolean.class), true); Assert.assertEquals(AttributeUtils.parse("1", Boolean.class), true); Assert.assertEquals(AttributeUtils.parse("0", Boolean.class), false); - Assert.assertEquals(AttributeUtils.parse("123456789123456789123456789123456789", BigInteger.class), new BigInteger( - "123456789123456789123456789123456789")); + Assert.assertEquals(AttributeUtils + .parse("123456789123456789123456789123456789", BigInteger.class), new BigInteger( + "123456789123456789123456789123456789")); Assert.assertEquals(AttributeUtils .parse("123456789123456789123456789123456789.123456789123456789123456789123456789", BigDecimal.class), new BigDecimal( - "123456789123456789123456789123456789.123456789123456789123456789123456789")); + "123456789123456789123456789123456789.123456789123456789123456789123456789")); } @Test @@ -105,8 +106,10 @@ public void testParsePrimitiveTypes() { @Test public void testParseArrayTypes() { - Assert.assertEquals(AttributeUtils.parse("[true, false, 1, 0, null]", Boolean[].class), new Boolean[] { true, false, true, false, null }); - Assert.assertEquals(AttributeUtils.parse("[true, false, 1, 0]", boolean[].class), new boolean[] { true, false, true, false }); + Assert.assertEquals(AttributeUtils + .parse("[true, false, 1, 0, null]", Boolean[].class), new Boolean[] { true, false, true, false, null }); + Assert.assertEquals(AttributeUtils + .parse("[true, false, 1, 0]", boolean[].class), new boolean[] { true, false, true, false }); Assert.assertEquals(AttributeUtils.parse("[-1, 3, null]", Integer[].class), new Integer[] { -1, 3, null }); Assert.assertEquals(AttributeUtils.parse("[-1, 3, null]", Integer[].class).getClass(), Integer[].class); @@ -121,20 +124,26 @@ public void testParseArrayTypes() { Assert.assertEquals(AttributeUtils.parse("[-1, 3, null]", Long[].class), new Long[] { -1l, 3l, null }); Assert.assertEquals(AttributeUtils.parse("[-1, 0, 2]", long[].class), new long[] { -1, 0, 2 }); - Assert.assertEquals(AttributeUtils.parse("[-1e6, 1, .001, 2000000., null]", Float[].class), new Float[] { -1e6f, 1.0f, .001f, 2e6f, null }); + Assert.assertEquals(AttributeUtils + .parse("[-1e6, 1, .001, 2000000., null]", Float[].class), new Float[] { -1e6f, 1.0f, .001f, 2e6f, null }); Assert.assertEquals(AttributeUtils.parse("[1]", float[].class).getClass(), float[].class); - Assert.assertEquals(AttributeUtils.parse("[-1e6, 1, .001, 2e6]", float[].class), new float[] { -1e6f, 1.0f, .001f, 2e6f }); + Assert.assertEquals(AttributeUtils + .parse("[-1e6, 1, .001, 2e6]", float[].class), new float[] { -1e6f, 1.0f, .001f, 2e6f }); - Assert.assertEquals(AttributeUtils.parse("[-1e6, 1, .001, 2000000., null]", Double[].class), new Double[] { -1e6, 1.0, .001, 2e6, null }); - Assert.assertEquals(AttributeUtils.parse("[-1e6, 1, .001, 2e6]", double[].class), new double[] { -1e6, 1.0, .001, 2e6 }); + Assert.assertEquals(AttributeUtils + .parse("[-1e6, 1, .001, 2000000., null]", Double[].class), new Double[] { -1e6, 1.0, .001, 2e6, null }); + Assert.assertEquals(AttributeUtils + .parse("[-1e6, 1, .001, 2e6]", double[].class), new double[] { -1e6, 1.0, .001, 2e6 }); Assert.assertEquals(AttributeUtils.parse("[-1e6, 1, .001, 2e6]", double[].class).getClass(), double[].class); - Assert.assertEquals(AttributeUtils.parse("[' true ', 'null', null]", String[].class), new String[] { " true ", "null", null }); - Assert.assertEquals(AttributeUtils.parse("['123456789123456789123456789123456789']", BigInteger[].class), new BigInteger[] { new BigInteger( - "123456789123456789123456789123456789") }); + Assert.assertEquals(AttributeUtils + .parse("[' true ', 'null', null]", String[].class), new String[] { " true ", "null", null }); + Assert.assertEquals(AttributeUtils + .parse("['123456789123456789123456789123456789']", BigInteger[].class), new BigInteger[] { new BigInteger( + "123456789123456789123456789123456789") }); Assert.assertEquals(AttributeUtils .parse("['123456789123456789123456789123456789.123456789123456789123456789123456789']", BigDecimal[].class), new BigDecimal[] { new BigDecimal( - "123456789123456789123456789123456789.123456789123456789123456789123456789") }); + "123456789123456789123456789123456789.123456789123456789123456789123456789") }); } @Test @@ -238,35 +247,42 @@ public void testParseDynamicTimestampTypesWithTimeZone() { .parse("<[2015-01-01T01:30:00]>", TimestampSet.class, DateTimeZone.forID("+01:30"))); // Maps - Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, null)); - Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, DateTimeZone.UTC)); - Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils - .parse("<[2015-01-01T01:30:00, val]>", TimestampStringMap.class, DateTimeZone.forID("+01:30"))); + Assert.assertEquals(AttributeUtils + .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils + .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, null)); + Assert.assertEquals(AttributeUtils + .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils + .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, DateTimeZone.UTC)); + Assert.assertEquals(AttributeUtils + .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils + .parse("<[2015-01-01T01:30:00, val]>", TimestampStringMap.class, DateTimeZone.forID("+01:30"))); } @Test public void testParseDynamicIntervalTypesWithTimeZone() { // Sets - Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, null)); - Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, DateTimeZone.UTC)); - Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils - .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00]>", IntervalSet.class, DateTimeZone.forID("-02:00"))); + Assert.assertEquals(AttributeUtils + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, null)); + Assert.assertEquals(AttributeUtils + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, DateTimeZone.UTC)); + Assert.assertEquals(AttributeUtils + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils + .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00]>", IntervalSet.class, DateTimeZone + .forID("-02:00"))); // Maps Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, null)); + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, null)); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, DateTimeZone.UTC)); + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, DateTimeZone.UTC)); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class), AttributeUtils - .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00, val]>", IntervalStringMap.class, DateTimeZone - .forID("-02:00"))); + .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00, val]>", IntervalStringMap.class, DateTimeZone + .forID("-02:00"))); } @Test(expectedExceptions = IllegalArgumentException.class) @@ -310,12 +326,14 @@ public void testGetPrimitiveTypeUnsupportedType() { public void testGetPrimitiveArray() { Assert.assertEquals((int[]) AttributeUtils.getPrimitiveArray(new Integer[] { 1, 2 }), new int[] { 1, 2 }); Assert.assertEquals((float[]) AttributeUtils.getPrimitiveArray(new Float[] { 1f, 2f }), new float[] { 1f, 2f }); - Assert.assertEquals((double[]) AttributeUtils.getPrimitiveArray(new Double[] { 1.0, 2.0 }), new double[] { 1.0, 2.0 }); + Assert.assertEquals((double[]) AttributeUtils + .getPrimitiveArray(new Double[] { 1.0, 2.0 }), new double[] { 1.0, 2.0 }); Assert.assertEquals((long[]) AttributeUtils.getPrimitiveArray(new Long[] { 1l, 2l }), new long[] { 1l, 2l }); Assert.assertEquals((char[]) AttributeUtils.getPrimitiveArray(new Character[] { 1, 2 }), new char[] { 1, 2 }); Assert.assertEquals((short[]) AttributeUtils.getPrimitiveArray(new Short[] { 1, 2 }), new short[] { 1, 2 }); Assert.assertEquals((byte[]) AttributeUtils.getPrimitiveArray(new Byte[] { 1, 2 }), new byte[] { 1, 2 }); - Assert.assertEquals((boolean[]) AttributeUtils.getPrimitiveArray(new Boolean[] { true, false }), new boolean[] { true, false }); + Assert.assertEquals((boolean[]) AttributeUtils + .getPrimitiveArray(new Boolean[] { true, false }), new boolean[] { true, false }); } @Test(expectedExceptions = IllegalArgumentException.class) @@ -579,7 +597,8 @@ public void testParseDateTimeOrTimestamp() { Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00", null), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00", DateTimeZone.UTC), 0.0); - Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T01:30:00", DateTimeZone.forID("+01:30")), 0.0); + Assert.assertEquals(AttributeUtils + .parseDateTimeOrTimestamp("1970-01-01T01:30:00", DateTimeZone.forID("+01:30")), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00+00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00Z"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00.000+00:00"), 0.0); @@ -622,17 +641,22 @@ public void testPrintDateTime() { Assert.assertEquals(AttributeUtils.printDateTime(d, DateTimeZone.UTC), dateInUTC); Assert.assertEquals(AttributeUtils.printDateTime(d, null), dateInUTC); Assert.assertEquals(AttributeUtils.printDateTime(d), "2003-01-01T08:00:00.000Z"); - Assert.assertEquals(AttributeUtils.printDateTime(d, DateTimeZone.forID("+00:30")), "2003-01-01T08:30:00.000+00:30"); - Assert.assertEquals(AttributeUtils.printDateTime(d, DateTimeZone.forID("+12:00")), "2003-01-01T20:00:00.000+12:00"); - Assert.assertEquals(AttributeUtils.printDateTime(d, DateTimeZone.forID("-12:00")), "2002-12-31T20:00:00.000-12:00"); + Assert.assertEquals(AttributeUtils + .printDateTime(d, DateTimeZone.forID("+00:30")), "2003-01-01T08:30:00.000+00:30"); + Assert.assertEquals(AttributeUtils + .printDateTime(d, DateTimeZone.forID("+12:00")), "2003-01-01T20:00:00.000+12:00"); + Assert.assertEquals(AttributeUtils + .printDateTime(d, DateTimeZone.forID("-12:00")), "2002-12-31T20:00:00.000-12:00"); Assert.assertEquals(AttributeUtils.printDateTime(AttributeUtils - .parseDateTime("2003-01-01T16:00:00", DateTimeZone.forID("+00:00")), DateTimeZone.forID("+12:00")), "2003-01-02T04:00:00.000+12:00"); + .parseDateTime("2003-01-01T16:00:00", DateTimeZone.forID("+00:00")), DateTimeZone + .forID("+12:00")), "2003-01-02T04:00:00.000+12:00"); } @Test public void testPrintArray() { - Assert.assertEquals(AttributeUtils.printArray(new String[] { null, "null", " b ", "\"c" }), "[null, \"null\", \" b \", \"\\\"c\"]"); + Assert.assertEquals(AttributeUtils + .printArray(new String[] { null, "null", " b ", "\"c" }), "[null, \"null\", \" b \", \"\\\"c\"]"); Assert.assertEquals(AttributeUtils.printArray(new Integer[] { -1, 2, 3, null }), "[-1, 2, 3, null]"); Assert.assertEquals(AttributeUtils.printArray(new int[] { -1, 2, 3 }), "[-1, 2, 3]"); Assert.assertEquals(AttributeUtils.printArray(new boolean[] { true, false, true }), "[true, false, true]"); diff --git a/store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index acf4542c..3605d86a 100644 --- a/store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -211,14 +211,14 @@ public EdgeIterable getEdges() { @Override public NodeIterable getNeighbors(Node node) { - return new NodeIterableWrapper(new NeighborsUndirectedIterator((BasicNode) node, - edgeStore.inOutIterator((BasicNode) node))); + return new NodeIterableWrapper( + new NeighborsUndirectedIterator((BasicNode) node, edgeStore.inOutIterator((BasicNode) node))); } @Override public NodeIterable getNeighbors(Node node, int type) { - return new NodeIterableWrapper(new NeighborsUndirectedIterator((BasicNode) node, - edgeStore.inOutIterator((BasicNode) node, type))); + return new NodeIterableWrapper( + new NeighborsUndirectedIterator((BasicNode) node, edgeStore.inOutIterator((BasicNode) node, type))); } @Override @@ -298,8 +298,8 @@ public boolean isDirected(Edge edge) { @Override public boolean isIncident(Edge edge1, Edge edge2) { - return edge1.getSource() == edge2.getSource() || edge1.getTarget() == edge2.getTarget() || edge1.getSource() == edge2 - .getTarget() || edge1.getTarget() == edge2.getSource(); + return edge1.getSource() == edge2.getSource() || edge1.getTarget() == edge2.getTarget() || edge1 + .getSource() == edge2.getTarget() || edge1.getTarget() == edge2.getSource(); } @Override diff --git a/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java b/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java index 37ce5f21..2f5c62bc 100644 --- a/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java +++ b/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java @@ -71,7 +71,8 @@ public void testGetDefaultIntervalWeight() { GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(42.0, new Interval(1.0, 2.0)); - Assert.assertEquals(e.getWeight(new Interval(2.1, 4.0)), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); + Assert.assertEquals(e + .getWeight(new Interval(2.1, 4.0)), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); } @Test @@ -81,7 +82,8 @@ public void testGetDefaultIntervalWeightWhenNotSet() { config.setEdgeWeightType(IntervalDoubleMap.class); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); - Assert.assertEquals(e.getWeight(new Interval(2.0, 4.0)), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); + Assert.assertEquals(e + .getWeight(new Interval(2.0, 4.0)), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); } @Test @@ -216,7 +218,8 @@ public void testGetDefaultWeightByGraphView() { config.setEdgeWeightType(TimestampDoubleMap.class); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); - Assert.assertEquals(e.getWeight(graphStore.mainGraphView), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); + Assert.assertEquals(e + .getWeight(graphStore.mainGraphView), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); } @Test @@ -256,7 +259,8 @@ public void testGetWeightNoValue() { GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setAttribute("weight", null); - Assert.assertEquals(e.getWeight(graphStore.getView()), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); + Assert.assertEquals(e + .getWeight(graphStore.getView()), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); } @Test diff --git a/store/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/store/src/test/java/org/gephi/graph/impl/ElementImplTest.java index 95345d3b..5f58a1ee 100644 --- a/store/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/store/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -1090,8 +1090,8 @@ private Column generateBasicColumn(GraphStore graphStore) { } private Column generateBasicBooleanColumn(GraphStore graphStore) { - graphStore.nodeTable.store.addColumn(new ColumnImpl("visible", Boolean.class, "Visible", null, Origin.DATA, - true, false)); + graphStore.nodeTable.store + .addColumn(new ColumnImpl("visible", Boolean.class, "Visible", null, Origin.DATA, true, false)); return graphStore.nodeTable.store.getColumn("visible"); } @@ -1112,14 +1112,14 @@ private Column generateBasicMapColumn(GraphStore graphStore) { } private Column generateTimestampColumn(GraphStore graphStore) { - graphStore.nodeTable.store.addColumn(new ColumnImpl("age", TimestampIntegerMap.class, "Age", null, Origin.DATA, - false, false)); + graphStore.nodeTable.store + .addColumn(new ColumnImpl("age", TimestampIntegerMap.class, "Age", null, Origin.DATA, false, false)); return graphStore.nodeTable.store.getColumn("age"); } private Column generateIntervalColumn(GraphStore graphStore) { - graphStore.nodeTable.store.addColumn(new ColumnImpl("age", IntervalIntegerMap.class, "Age", null, Origin.DATA, - false, false)); + graphStore.nodeTable.store + .addColumn(new ColumnImpl("age", IntervalIntegerMap.class, "Age", null, Origin.DATA, false, false)); return graphStore.nodeTable.store.getColumn("age"); } diff --git a/store/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/store/src/test/java/org/gephi/graph/impl/GraphGenerator.java index fbe07c7d..43cae8d6 100644 --- a/store/src/test/java/org/gephi/graph/impl/GraphGenerator.java +++ b/store/src/test/java/org/gephi/graph/impl/GraphGenerator.java @@ -148,8 +148,9 @@ public static EdgeImpl[] generateEdgeList(NodeStore nodeStore, int edgeCount, in NodeImpl source = nodeStore.get(sourceId); NodeImpl target = nodeStore.get(targetId); EdgeImpl edge = new EdgeImpl(String.valueOf(c), graphStore, source, target, type, 1.0, directed); - if (!leafs.contains(sourceId) && !leafs.contains(targetId) && (allowSelfLoops || (!allowSelfLoops && source != target)) && (allowParallel || !idSet - .contains(edge.getLongId()))) { + if (!leafs.contains(sourceId) && !leafs + .contains(targetId) && (allowSelfLoops || (!allowSelfLoops && source != target)) && (allowParallel || !idSet + .contains(edge.getLongId()))) { edgeList.add(edge); c++; idSet.add(edge.getLongId()); @@ -182,10 +183,11 @@ public static BasicGraphStore.BasicEdge[] generateBasicEdgeList(BasicGraphStore. int targetId = r.nextInt(nodeCount); BasicGraphStore.BasicNode source = nodeStore.get(String.valueOf(sourceId)); BasicGraphStore.BasicNode target = nodeStore.get(String.valueOf(targetId)); - BasicGraphStore.BasicEdge edge = new BasicGraphStore.BasicEdge(String.valueOf(c), source, target, type, - 1.0, directed); - if (!leafs.contains(sourceId) && !leafs.contains(targetId) && (allowSelfLoops || (!allowSelfLoops && source != target)) && !idSet - .contains(edge.getStringId())) { + BasicGraphStore.BasicEdge edge = new BasicGraphStore.BasicEdge(String.valueOf(c), source, target, type, 1.0, + directed); + if (!leafs.contains(sourceId) && !leafs + .contains(targetId) && (allowSelfLoops || (!allowSelfLoops && source != target)) && !idSet + .contains(edge.getStringId())) { edgeList.add(edge); c++; idSet.add(edge.getStringId()); @@ -218,9 +220,9 @@ public static EdgeImpl[] generateMixedEdgeList(NodeStore nodeStore, int edgeCoun NodeImpl source = nodeStore.get(sourceId); NodeImpl target = nodeStore.get(targetId); EdgeImpl edge = new EdgeImpl(String.valueOf(c), source, target, type, 1.0, false); - if ((allowSelfLoops || (!allowSelfLoops && source != target)) && !idSet.contains(EdgeStore - .getLongId(edge.source, edge.target, true)) && !idSet.contains(EdgeStore - .getLongId(edge.target, edge.source, true))) { + if ((allowSelfLoops || (!allowSelfLoops && source != target)) && !idSet + .contains(EdgeStore.getLongId(edge.source, edge.target, true)) && !idSet + .contains(EdgeStore.getLongId(edge.target, edge.source, true))) { edgeList.add(edge); c++; idSet.add(edge.getLongId()); @@ -240,8 +242,8 @@ public static BasicGraphStore.BasicEdge[] generateBasicMixedEdgeList(BasicGraphS int targetId = r.nextInt(nodeCount); BasicGraphStore.BasicNode source = nodeStore.get(String.valueOf(sourceId)); BasicGraphStore.BasicNode target = nodeStore.get(String.valueOf(targetId)); - BasicGraphStore.BasicEdge edge = new BasicGraphStore.BasicEdge(String.valueOf(c), source, target, type, - 1.0, true); + BasicGraphStore.BasicEdge edge = new BasicGraphStore.BasicEdge(String.valueOf(c), source, target, type, 1.0, + true); if ((allowSelfLoops || (!allowSelfLoops && source != target)) && !idSet.contains(edge.getStringId())) { edgeList.add(edge); c++; @@ -253,11 +255,11 @@ public static BasicGraphStore.BasicEdge[] generateBasicMixedEdgeList(BasicGraphS int targetId = r.nextInt(nodeCount); BasicGraphStore.BasicNode source = nodeStore.get(String.valueOf(sourceId)); BasicGraphStore.BasicNode target = nodeStore.get(String.valueOf(targetId)); - BasicGraphStore.BasicEdge edge = new BasicGraphStore.BasicEdge(String.valueOf(c), source, target, type, - 1.0, false); - if ((allowSelfLoops || (!allowSelfLoops && source != target)) && !idSet.contains(BasicEdgeStore - .getStringId(edge.source, edge.target, true)) && !idSet.contains(BasicEdgeStore - .getStringId(edge.target, edge.source, true))) { + BasicGraphStore.BasicEdge edge = new BasicGraphStore.BasicEdge(String.valueOf(c), source, target, type, 1.0, + false); + if ((allowSelfLoops || (!allowSelfLoops && source != target)) && !idSet + .contains(BasicEdgeStore.getStringId(edge.source, edge.target, true)) && !idSet + .contains(BasicEdgeStore.getStringId(edge.target, edge.source, true))) { edgeList.add(edge); c++; idSet.add(edge.getStringId()); @@ -438,7 +440,8 @@ public static GraphStore generateTinyGraphStoreWithSelfLoop() { public static GraphStore generateSmallGraphStore() { int edgeCount = 100; GraphStore graphStore = new GraphModelImpl().store; - NodeImpl[] nodes = generateNodeList(Math.max((int) Math.ceil(Math.sqrt(edgeCount * 2)), (int) (edgeCount / 10.0)), graphStore); + NodeImpl[] nodes = generateNodeList(Math + .max((int) Math.ceil(Math.sqrt(edgeCount * 2)), (int) (edgeCount / 10.0)), graphStore); graphStore.addAllNodes(Arrays.asList(nodes)); EdgeImpl[] edges = generateEdgeList(graphStore.nodeStore, edgeCount, 0, true, true, false); graphStore.addAllEdges(Arrays.asList(edges)); @@ -452,7 +455,8 @@ public static GraphStore generateSmallMixedGraphStore() { public static GraphStore generateSmallMixedGraphStore(int type) { int edgeCount = 100; GraphStore graphStore = new GraphModelImpl().store; - NodeImpl[] nodes = generateNodeList(Math.max((int) Math.ceil(Math.sqrt(edgeCount * 2)), (int) (edgeCount / 10.0)), graphStore); + NodeImpl[] nodes = generateNodeList(Math + .max((int) Math.ceil(Math.sqrt(edgeCount * 2)), (int) (edgeCount / 10.0)), graphStore); graphStore.addAllNodes(Arrays.asList(nodes)); EdgeImpl[] edges = generateMixedEdgeList(graphStore.nodeStore, edgeCount, type, true); graphStore.addAllEdges(Arrays.asList(edges)); @@ -462,7 +466,8 @@ public static GraphStore generateSmallMixedGraphStore(int type) { public static GraphStore generateSmallMultiTypeGraphStore() { int edgeCount = 100; GraphStore graphStore = new GraphModelImpl().store; - NodeImpl[] nodes = generateNodeList(Math.max((int) Math.ceil(Math.sqrt(edgeCount * 2)), (int) (edgeCount / 10.0)), graphStore); + NodeImpl[] nodes = generateNodeList(Math + .max((int) Math.ceil(Math.sqrt(edgeCount * 2)), (int) (edgeCount / 10.0)), graphStore); graphStore.addAllNodes(Arrays.asList(nodes)); EdgeImpl[] edges = generateMultiTypeEdgeList(graphStore.nodeStore, edgeCount, 3, true, true); graphStore.addAllEdges(Arrays.asList(edges)); @@ -472,7 +477,8 @@ public static GraphStore generateSmallMultiTypeGraphStore() { public static GraphStore generateSmallUndirectedGraphStore() { int edgeCount = 100; GraphStore graphStore = new GraphModelImpl().store; - NodeImpl[] nodes = generateNodeList(Math.max((int) Math.ceil(Math.sqrt(edgeCount * 2)), (int) (edgeCount / 10.0)), graphStore); + NodeImpl[] nodes = generateNodeList(Math + .max((int) Math.ceil(Math.sqrt(edgeCount * 2)), (int) (edgeCount / 10.0)), graphStore); graphStore.addAllNodes(Arrays.asList(nodes)); EdgeImpl[] edges = generateEdgeList(graphStore.nodeStore, edgeCount, 0, false, true, false); graphStore.addAllEdges(Arrays.asList(edges)); diff --git a/store/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/store/src/test/java/org/gephi/graph/impl/GraphModelTest.java index d5d7f9cf..9cee9e0f 100644 --- a/store/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/store/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -585,7 +585,8 @@ public void testSetConfigurationEdgeWeightColumnFalse() { config.setEdgeWeightColumn(Boolean.FALSE); graphModelImpl.setConfiguration(config); Assert.assertFalse(graphModelImpl.store.edgeTable.hasColumn("weight")); - Assert.assertNotEquals(graphModelImpl.store.edgeTable.addColumn("foo", Integer.class).getIndex(), GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + Assert.assertNotEquals(graphModelImpl.store.edgeTable.addColumn("foo", Integer.class) + .getIndex(), GraphStoreConfiguration.EDGE_WEIGHT_INDEX); } @Test @@ -598,7 +599,8 @@ public void testSetConfigurationEdgeWeightColumnTrue() { config.setEdgeWeightColumn(Boolean.TRUE); graphModelImpl.setConfiguration(config); Assert.assertTrue(graphModelImpl.store.edgeTable.hasColumn("weight")); - Assert.assertEquals(graphModelImpl.store.edgeTable.getColumn("weight").getIndex(), GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + Assert.assertEquals(graphModelImpl.store.edgeTable.getColumn("weight") + .getIndex(), GraphStoreConfiguration.EDGE_WEIGHT_INDEX); } @Test diff --git a/store/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java b/store/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java index 84b35d39..145dedf2 100644 --- a/store/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java +++ b/store/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java @@ -86,11 +86,16 @@ public void testParseIntervalSet() throws ParseException { // Doubles: assertEquals(buildIntervalSet(new Interval(1, 2)), IntervalsParser.parseIntervalSet("[1, 2]")); - assertEquals(buildIntervalSet(new Interval(1, 2), new Interval(2, 3)), IntervalsParser.parseIntervalSet("<[1, 2]; [2,3]>")); - assertEquals(buildIntervalSet(new Interval(1, 2), new Interval(2, 31)), IntervalsParser.parseIntervalSet("<[1, 2]; [2,31.]>")); - assertEquals(buildIntervalSet(new Interval(1, 2), new Interval(2, 31)), IntervalsParser.parseIntervalSet("<[1, 2]; [2,31.0)")); - assertEquals(buildIntervalSet(new Interval(-5000, -1), new Interval(0, 0.5)), IntervalsParser.parseIntervalSet("(-5000,-1][0, .5)")); - assertEquals(buildIntervalSet(new Interval(-5000, -1), new Interval(0, 0.5)), IntervalsParser.parseIntervalSet("(-5e3,-1)(0, .5)")); + assertEquals(buildIntervalSet(new Interval(1, 2), new Interval(2, 3)), IntervalsParser + .parseIntervalSet("<[1, 2]; [2,3]>")); + assertEquals(buildIntervalSet(new Interval(1, 2), new Interval(2, 31)), IntervalsParser + .parseIntervalSet("<[1, 2]; [2,31.]>")); + assertEquals(buildIntervalSet(new Interval(1, 2), new Interval(2, 31)), IntervalsParser + .parseIntervalSet("<[1, 2]; [2,31.0)")); + assertEquals(buildIntervalSet(new Interval(-5000, -1), new Interval(0, 0.5)), IntervalsParser + .parseIntervalSet("(-5000,-1][0, .5)")); + assertEquals(buildIntervalSet(new Interval(-5000, -1), new Interval(0, 0.5)), IntervalsParser + .parseIntervalSet("(-5e3,-1)(0, .5)")); // Dates: assertEquals(buildIntervalSet(new Interval(parseDateIntoTimestamp("2015-01-01"), @@ -100,9 +105,11 @@ public void testParseIntervalSet() throws ParseException { // Date times: assertEquals(buildIntervalSet(new Interval(parseDateTimeIntoTimestamp("2015-01-01 21:12:05"), - parseDateTimeIntoTimestamp("2015-01-02 00:00:00"))), IntervalsParser.parseIntervalSet("[2015-01-01T21:12:05, 2015-01-02]")); + parseDateTimeIntoTimestamp("2015-01-02 00:00:00"))), IntervalsParser + .parseIntervalSet("[2015-01-01T21:12:05, 2015-01-02]")); assertEquals(buildIntervalSet(new Interval(parseDateTimeMillisIntoTimestamp("2015-01-01 21:12:05.121"), - parseDateTimeMillisIntoTimestamp("2015-01-02 00:00:01.999"))), IntervalsParser.parseIntervalSet("[2015-01-01T21:12:05.121, 2015-01-02T00:00:01.999]")); + parseDateTimeMillisIntoTimestamp("2015-01-02 00:00:01.999"))), IntervalsParser + .parseIntervalSet("[2015-01-01T21:12:05.121, 2015-01-02T00:00:01.999]")); } @Test(expectedExceptions = IllegalArgumentException.class) @@ -164,7 +171,8 @@ public void testParseIntervalMapString() { expected.put(new Interval(5, 6), "Value 3"); expected.put(new Interval(6, 7), " Value 4 "); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(String.class, "[1, 2, Value1]; [3, 5, 'Value2']; [5, 6, Value 3]; [6, 7, \" Value 4 \"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(String.class, "[1, 2, Value1]; [3, 5, 'Value2']; [5, 6, Value 3]; [6, 7, \" Value 4 \"]")); } @Test @@ -175,8 +183,10 @@ public void testParseIntervalMapByte() { expected.put(new Interval(5, 6), (byte) 3); expected.put(new Interval(6, 7), (byte) 4); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(Byte.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(byte.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(Byte.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(byte.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); } @Test @@ -187,12 +197,14 @@ public void testParseIntervalMapShort() { expected.put(new Interval(5, 6), (short) 3); expected.put(new Interval(6, 7), (short) 4); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(Short.class, "[1, 2, 1.1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals - // are - // ignored - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(short.class, "[1, 2, 1.1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals - // are - // ignored + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(Short.class, "[1, 2, 1.1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals + // are + // ignored + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(short.class, "[1, 2, 1.1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals + // are + // ignored } @Test @@ -203,12 +215,14 @@ public void testParseIntervalMapInteger() { expected.put(new Interval(5, 6), 3); expected.put(new Interval(6, 7), 4); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(Integer.class, "[1, 2, 1.]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals - // are - // ignored - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(int.class, "[1, 2, 1.]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals - // are - // ignored + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(Integer.class, "[1, 2, 1.]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals + // are + // ignored + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(int.class, "[1, 2, 1.]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals + // are + // ignored } @Test @@ -219,12 +233,14 @@ public void testParseIntervalMapLong() { expected.put(new Interval(5, 6), 3l); expected.put(new Interval(6, 7), 4l); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(Long.class, "[1, 2, 1.0]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals - // are - // ignored - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(long.class, "[1, 2, 1.0]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals - // are - // ignored + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(Long.class, "[1, 2, 1.0]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals + // are + // ignored + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(long.class, "[1, 2, 1.0]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]"));// Decimals + // are + // ignored } @Test @@ -235,8 +251,10 @@ public void testParseIntervalMapFloat() { expected.put(new Interval(5, 6), 3f); expected.put(new Interval(6, 7), 4f); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(Float.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(float.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(Float.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(float.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); } @Test @@ -247,8 +265,10 @@ public void testParseIntervalMapDouble() { expected.put(new Interval(5, 6), 3d); expected.put(new Interval(6, 7), 4d); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(Double.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(double.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(Double.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(double.class, "[1, 2, 1]; [3, 5, 2]; [5, 6, '3']; [6, 7, \"4\"]")); } @Test @@ -259,8 +279,10 @@ public void testParseIntervalMapBoolean() { expected.put(new Interval(5, 6), false); expected.put(new Interval(6, 7), true); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(Boolean.class, "[1, 2, true]; [3, 5, false]; [5, 6, '0']; [6, 7, \"1\"]")); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(boolean.class, "[1, 2, true]; [3, 5, false]; [5, 6, 0]; [6, 7, 1]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(Boolean.class, "[1, 2, true]; [3, 5, false]; [5, 6, '0']; [6, 7, \"1\"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(boolean.class, "[1, 2, true]; [3, 5, false]; [5, 6, 0]; [6, 7, 1]")); } @Test @@ -271,8 +293,10 @@ public void testParseIntervalMapChar() { expected.put(new Interval(5, 6), 'c'); expected.put(new Interval(6, 7), 'd'); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(Character.class, "[1, 2, a]; [3, 5, b]; [5, 6, 'c']; [6, 7, \"d\"]")); - assertEqualIntervalMaps(expected, IntervalsParser.parseIntervalMap(char.class, "[1, 2, a]; [3, 5, b]; [5, 6, 'c']; [6, 7, \"d\"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(Character.class, "[1, 2, a]; [3, 5, b]; [5, 6, 'c']; [6, 7, \"d\"]")); + assertEqualIntervalMaps(expected, IntervalsParser + .parseIntervalMap(char.class, "[1, 2, a]; [3, 5, b]; [5, 6, 'c']; [6, 7, \"d\"]")); } @Test(expectedExceptions = IllegalArgumentException.class) diff --git a/store/src/test/java/org/gephi/graph/impl/SerializationTest.java b/store/src/test/java/org/gephi/graph/impl/SerializationTest.java index 5d6bc39f..08a31a34 100644 --- a/store/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/store/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -772,22 +772,23 @@ public void testList() throws IOException, ClassNotFoundException { mixedList.add(42); Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(mixedList)), mixedList); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new IntArrayList(new int[] { 42 }))), new IntArrayList( - new int[] { 42 })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new FloatArrayList(new float[] { 42f }))), new FloatArrayList( - new float[] { 42f })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new IntArrayList(new int[] { 42 }))), new IntArrayList(new int[] { 42 })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new FloatArrayList(new float[] { 42f }))), new FloatArrayList(new float[] { 42f })); Assert.assertEquals(new Serialization(null).deserialize(ser .serialize(new DoubleArrayList(new double[] { 42.0 }))), new DoubleArrayList(new double[] { 42.0 })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new ShortArrayList(new short[] { 42 }))), new ShortArrayList( - new short[] { 42 })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new ByteArrayList(new byte[] { 42 }))), new ByteArrayList( - new byte[] { 42 })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new LongArrayList(new long[] { 42l }))), new LongArrayList( - new long[] { 42l })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new BooleanArrayList( - new boolean[] { true }))), new BooleanArrayList(new boolean[] { true })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new CharArrayList(new char[] { 'a' }))), new CharArrayList( - new char[] { 'a' })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new ShortArrayList(new short[] { 42 }))), new ShortArrayList(new short[] { 42 })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new ByteArrayList(new byte[] { 42 }))), new ByteArrayList(new byte[] { 42 })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new LongArrayList(new long[] { 42l }))), new LongArrayList(new long[] { 42l })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new BooleanArrayList(new boolean[] { true }))), new BooleanArrayList( + new boolean[] { true })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new CharArrayList(new char[] { 'a' }))), new CharArrayList(new char[] { 'a' })); } @Test @@ -810,22 +811,24 @@ public void testSet() throws IOException, ClassNotFoundException { mixedSet.add(42); Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(mixedSet)), mixedSet); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new IntOpenHashSet(new int[] { 42 }))), new IntOpenHashSet( - new int[] { 42 })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new IntOpenHashSet(new int[] { 42 }))), new IntOpenHashSet(new int[] { 42 })); Assert.assertEquals(new Serialization(null).deserialize(ser .serialize(new FloatOpenHashSet(new float[] { 42f }))), new FloatOpenHashSet(new float[] { 42f })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new DoubleOpenHashSet( - new double[] { 42.0 }))), new DoubleOpenHashSet(new double[] { 42.0 })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new ShortOpenHashSet(new short[] { 42 }))), new ShortOpenHashSet( - new short[] { 42 })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new ByteOpenHashSet(new byte[] { 42 }))), new ByteOpenHashSet( - new byte[] { 42 })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new LongOpenHashSet(new long[] { 42l }))), new LongOpenHashSet( - new long[] { 42l })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new BooleanOpenHashSet( - new boolean[] { true }))), new BooleanOpenHashSet(new boolean[] { true })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new CharOpenHashSet(new char[] { 'a' }))), new CharOpenHashSet( - new char[] { 'a' })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new DoubleOpenHashSet(new double[] { 42.0 }))), new DoubleOpenHashSet( + new double[] { 42.0 })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new ShortOpenHashSet(new short[] { 42 }))), new ShortOpenHashSet(new short[] { 42 })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new ByteOpenHashSet(new byte[] { 42 }))), new ByteOpenHashSet(new byte[] { 42 })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new LongOpenHashSet(new long[] { 42l }))), new LongOpenHashSet(new long[] { 42l })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new BooleanOpenHashSet(new boolean[] { true }))), new BooleanOpenHashSet( + new boolean[] { true })); + Assert.assertEquals(new Serialization(null).deserialize(ser + .serialize(new CharOpenHashSet(new char[] { 'a' }))), new CharOpenHashSet(new char[] { 'a' })); } @Test @@ -848,27 +851,33 @@ public void testMap() throws IOException, ClassNotFoundException { mixedMap.put(42, 42); Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(mixedMap)), mixedMap); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new Int2ObjectOpenHashMap( - new int[] { 42 }, new Object[] { "foo" }))), new Int2ObjectOpenHashMap(new int[] { 42 }, - new Object[] { "foo" })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new Float2ObjectOpenHashMap( - new float[] { 42f }, new Object[] { "foo" }))), new Float2ObjectOpenHashMap(new float[] { 42f }, - new Object[] { "foo" })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new Double2ObjectOpenHashMap( - new double[] { 42.0 }, new Object[] { "foo" }))), new Double2ObjectOpenHashMap(new double[] { 42.0 }, - new Object[] { "foo" })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new Short2ObjectOpenHashMap( - new short[] { 42 }, new Object[] { "foo" }))), new Short2ObjectOpenHashMap(new short[] { 42 }, - new Object[] { "foo" })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new Byte2ObjectOpenHashMap( - new byte[] { 42 }, new Object[] { "foo" }))), new Byte2ObjectOpenHashMap(new byte[] { 42 }, - new Object[] { "foo" })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new Long2ObjectOpenHashMap( - new long[] { 42l }, new Object[] { "foo" }))), new Long2ObjectOpenHashMap(new long[] { 42l }, - new Object[] { "foo" })); - Assert.assertEquals(new Serialization(null).deserialize(ser.serialize(new Char2ObjectOpenHashMap( - new char[] { 'a' }, new Object[] { "foo" }))), new Char2ObjectOpenHashMap(new char[] { 'a' }, - new Object[] { "foo" })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new Int2ObjectOpenHashMap(new int[] { 42 }, + new Object[] { "foo" }))), new Int2ObjectOpenHashMap(new int[] { 42 }, new Object[] { "foo" })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new Float2ObjectOpenHashMap(new float[] { 42f }, + new Object[] { "foo" }))), new Float2ObjectOpenHashMap(new float[] { 42f }, + new Object[] { "foo" })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new Double2ObjectOpenHashMap(new double[] { 42.0 }, + new Object[] { "foo" }))), new Double2ObjectOpenHashMap(new double[] { 42.0 }, + new Object[] { "foo" })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new Short2ObjectOpenHashMap(new short[] { 42 }, + new Object[] { "foo" }))), new Short2ObjectOpenHashMap(new short[] { 42 }, + new Object[] { "foo" })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new Byte2ObjectOpenHashMap(new byte[] { 42 }, + new Object[] { "foo" }))), new Byte2ObjectOpenHashMap(new byte[] { 42 }, + new Object[] { "foo" })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new Long2ObjectOpenHashMap(new long[] { 42l }, + new Object[] { "foo" }))), new Long2ObjectOpenHashMap(new long[] { 42l }, + new Object[] { "foo" })); + Assert.assertEquals(new Serialization(null) + .deserialize(ser.serialize(new Char2ObjectOpenHashMap(new char[] { 'a' }, + new Object[] { "foo" }))), new Char2ObjectOpenHashMap(new char[] { 'a' }, + new Object[] { "foo" })); } @Test diff --git a/store/src/test/java/org/gephi/graph/impl/TableImplTest.java b/store/src/test/java/org/gephi/graph/impl/TableImplTest.java index d71c87d0..57e89eb1 100644 --- a/store/src/test/java/org/gephi/graph/impl/TableImplTest.java +++ b/store/src/test/java/org/gephi/graph/impl/TableImplTest.java @@ -183,8 +183,7 @@ public void testStandardizePrimitiveType() { public void testStandardizeArrayType() { TableImpl table = new TableImpl<>(Node.class, false); Column col = table.addColumn("test_col"/* - * Id does not allow non-simple - * types + * Id does not allow non-simple types */, Integer[].class); Assert.assertEquals(col.getTypeClass(), int[].class); } @@ -195,8 +194,7 @@ public void testStandardizeArrayDefaultValue() { Integer[] t = new Integer[] { 1, 2 }; Column col = table.addColumn("test_col"/* - * Id does not allow non-simple - * types + * Id does not allow non-simple types */, null, Integer[].class, Origin.DATA, t, false); Object d = col.getDefaultValue(); Assert.assertEquals(d.getClass(), int[].class); diff --git a/store/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java b/store/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java index 6fe70b4c..674d7507 100644 --- a/store/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java +++ b/store/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java @@ -140,7 +140,8 @@ public void testParseTimestampMapString() { expected.put(5.0, "Value 3"); expected.put(6.0, " Value 4 "); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(String.class, "[1, Value1]; [3, 'Value2']; [5, Value 3]; [6, \" Value 4 \"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(String.class, "[1, Value1]; [3, 'Value2']; [5, Value 3]; [6, \" Value 4 \"]")); } @Test @@ -151,8 +152,10 @@ public void testParseTimestampMapByte() { expected.put(6.0, (byte) 3); expected.put(7.0, (byte) 4); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(Byte.class, "[1, 1]; [3, 2]; [6, '3']; [7, \"4\"]")); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(byte.class, "[1, 1]; [3, 2]; [6, '3']; [7, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(Byte.class, "[1, 1]; [3, 2]; [6, '3']; [7, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(byte.class, "[1, 1]; [3, 2]; [6, '3']; [7, \"4\"]")); } @Test @@ -163,8 +166,10 @@ public void testParseTimestampMapShort() { expected.put(5.0, (short) 3); expected.put(6.0, (short) 4); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(Short.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(short.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(Short.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(short.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); } @Test @@ -175,8 +180,10 @@ public void testParseTimestampMapInteger() { expected.put(5.0, 3); expected.put(6.0, 4); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(Integer.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(int.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(Integer.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(int.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); } @Test @@ -187,8 +194,10 @@ public void testParseTimestampMapLong() { expected.put(5.0, 3l); expected.put(6.0, 4l); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(Long.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(long.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(Long.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(long.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); } @Test @@ -199,8 +208,10 @@ public void testParseTimestampMapFloat() { expected.put(5.0, 3f); expected.put(6.0, 4f); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(Float.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(float.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(Float.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(float.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); } @Test @@ -211,8 +222,10 @@ public void testParseTimestampMapDouble() { expected.put(5.0, 3d); expected.put(6.0, 4d); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(Double.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(double.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(Double.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(double.class, "[1, 1]; [3, 2]; [5, '3']; [6, \"4\"]")); } @Test @@ -223,8 +236,10 @@ public void testParseTimestampMapBoolean() { expected.put(5.0, false); expected.put(6.0, true); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(Boolean.class, "[1, true]; [3, false]; [5, '0']; [6, \"1\"]")); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(boolean.class, "[1, true]; [3, false]; [5, '0']; [6, \"1\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(Boolean.class, "[1, true]; [3, false]; [5, '0']; [6, \"1\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(boolean.class, "[1, true]; [3, false]; [5, '0']; [6, \"1\"]")); } @Test @@ -235,8 +250,10 @@ public void testParseTimestampMapChar() { expected.put(5.0, 'c'); expected.put(6.0, 'd'); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(Character.class, "[1, a]; [3, b]; [5, 'c']; [6, \"d\"]")); - assertEqualTimestampMaps(expected, TimestampsParser.parseTimestampMap(char.class, "[1, a]; [3, b]; [5, 'c']; [6, \"d\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(Character.class, "[1, a]; [3, b]; [5, 'c']; [6, \"d\"]")); + assertEqualTimestampMaps(expected, TimestampsParser + .parseTimestampMap(char.class, "[1, a]; [3, b]; [5, 'c']; [6, \"d\"]")); } @Test(expectedExceptions = IllegalArgumentException.class) From d7c7f662db448a3cda18625370e20a77afe882e4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 16:08:06 +0100 Subject: [PATCH 025/271] Also format the benchmark folder --- store/pom.xml | 6 ++- .../benchmark/EdgeStoreBenchmarkTest.java | 41 +++++++++------ .../benchmark/NodeStoreBenchmarkTest.java | 15 +++--- .../benchmarks/DataStructureBenchmark.java | 7 ++- .../benchmarks/EdgeStoreBenchmark.java | 14 ++--- .../benchmark/benchmarks/KleinbergGraph.java | 11 ++-- .../benchmark/benchmarks/RandomGraph.java | 6 +-- .../graph/benchmark/nanobench/NanoBench.java | 52 +++++++++---------- .../java/org/gephi/graph/api/package.html | 4 +- .../org/gephi/graph/api/types/package.html | 4 +- .../java/org/gephi/graph/spi/package.html | 4 +- 11 files changed, 90 insertions(+), 74 deletions(-) diff --git a/store/pom.xml b/store/pom.xml index 5a322bc7..2382e3c0 100644 --- a/store/pom.xml +++ b/store/pom.xml @@ -279,7 +279,11 @@ formatter-maven-plugin ${project.basedir}/formatter-config.xml - + + ${project.build.sourceDirectory} + ${project.build.testSourceDirectory} + ${project.basedir}/src/benchmark/java + diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java b/store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java index c35570b4..2b895204 100644 --- a/store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java @@ -23,61 +23,70 @@ public class EdgeStoreBenchmarkTest { @Test public void testPushStore() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; + int[] n = { 100, 1000, 5000 }; + double[] p = { 0.01, 0.1, 0.3 }; for (int nodes : n) { for (double prob : p) { int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench - .create().measurements(2).measure("push edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().pushEdgeStore(nodes, prob)); + NanoBench.create().measurements(2) + .measure("push edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() + .pushEdgeStore(nodes, prob)); } } } @Test public void testIterateStore() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; + int[] n = { 100, 1000, 5000 }; + double[] p = { 0.01, 0.1, 0.3 }; for (int nodes : n) { for (double prob : p) { int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2).measure("iterate edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().iterateEdgeStore(nodes, prob)); + NanoBench.create().measurements(2) + .measure("iterate edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() + .iterateEdgeStore(nodes, prob)); } } } @Test public void testIterateOutNeighbors() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; + int[] n = { 100, 1000, 5000 }; + double[] p = { 0.01, 0.1, 0.3 }; for (int nodes : n) { for (double prob : p) { int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2).measure("iterate neighbors list out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().iterateEdgeStoreNeighborsOut(nodes, prob)); + NanoBench.create().measurements(2) + .measure("iterate neighbors list out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() + .iterateEdgeStoreNeighborsOut(nodes, prob)); } } } @Test public void testIterateInOutNeighbors() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; + int[] n = { 100, 1000, 5000 }; + double[] p = { 0.01, 0.1, 0.3 }; for (int nodes : n) { for (double prob : p) { int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2).measure("iterate neighbors list in&out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().iterateEdgeStoreNeighborsInOut(nodes, prob)); + NanoBench.create().measurements(2) + .measure("iterate neighbors list in&out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() + .iterateEdgeStoreNeighborsInOut(nodes, prob)); } } } @Test public void testResetEdgeStore() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; + int[] n = { 100, 1000, 5000 }; + double[] p = { 0.01, 0.1, 0.3 }; for (int nodes : n) { for (double prob : p) { int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2).measure("reset edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().resetEdgeStore(nodes, prob)); + NanoBench.create().measurements(2) + .measure("reset edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() + .resetEdgeStore(nodes, prob)); } } } diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java b/store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java index 41096748..902807ad 100644 --- a/store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java @@ -23,25 +23,28 @@ public class NodeStoreBenchmarkTest { @Test public void testPushStore() { - int[] n = {100, 1000, 10000, 100000}; + int[] n = { 100, 1000, 10000, 100000 }; for (int nodes : n) { - NanoBench.create().measurements(10).measure("push node store " + nodes, new NodeStoreBenchmark().pushStore(nodes)); + NanoBench.create().measurements(10) + .measure("push node store " + nodes, new NodeStoreBenchmark().pushStore(nodes)); } } @Test public void testIterateStore() { - int[] n = {100, 1000, 10000, 100000}; + int[] n = { 100, 1000, 10000, 100000 }; for (int nodes : n) { - NanoBench.create().cpuOnly().measurements(10).measure("iterate node store " + nodes, new NodeStoreBenchmark().iterateStore(nodes)); + NanoBench.create().cpuOnly().measurements(10) + .measure("iterate node store " + nodes, new NodeStoreBenchmark().iterateStore(nodes)); } } @Test public void testResetNodeStore() { - int[] n = {100, 1000, 10000, 100000}; + int[] n = { 100, 1000, 10000, 100000 }; for (int nodes : n) { - NanoBench.create().measurements(10).measure("reset node store "+nodes, new NodeStoreBenchmark().resetNodeStore(nodes)); + NanoBench.create().measurements(10) + .measure("reset node store " + nodes, new NodeStoreBenchmark().resetNodeStore(nodes)); } } } diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java index bb34090d..8f32672a 100644 --- a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java @@ -71,8 +71,7 @@ public Runnable openHashMapMemory() { } /** - * Insertion and memory usage for Int2ObjectOpenHashMap without original - * capcity + * Insertion and memory usage for Int2ObjectOpenHashMap without original capcity */ public Runnable dynamicOpenHashMapMemory() { return () -> { @@ -197,7 +196,7 @@ public Runnable rbHashMapIteration() { } public Runnable arrayIteration() { - //Create array + // Create array int nodes = NODES; final Object[] array = new Object[nodes]; for (int i = 0; i < nodes; i++) { @@ -213,7 +212,7 @@ public Runnable arrayIteration() { } public Runnable linkedListIteration() { - //Create array + // Create array int nodes = NODES; final LinkedList list = new LinkedList(); for (int i = 0; i < nodes; i++) { diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java index 8e01dd4d..654db3ed 100644 --- a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java @@ -15,7 +15,7 @@ public class EdgeStoreBenchmark { private Object object; - + public Runnable pushEdgeStore(int nodes, double prob) { final Configuration config = new Configuration(); config.setEdgeIdType(Integer.class); @@ -25,10 +25,10 @@ public Runnable pushEdgeStore(int nodes, double prob) { final List nodeList = graph.getNodes(); final List edgeList = graph.getEdges(); graph.getStore().addAllNodes(nodeList); - + Runnable runnable = () -> { edgeStore.clear(); - for(Edge edge : edgeList) { + for (Edge edge : edgeList) { edgeStore.add(edge); } }; @@ -41,7 +41,7 @@ public Runnable iterateEdgeStore(int nodes, double prob) { config.setNodeIdType(Integer.class); final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - + Runnable runnable = () -> { Iterator itr = edgeStore.iterator(); for (; itr.hasNext();) { @@ -58,7 +58,7 @@ public Runnable iterateEdgeStoreNeighborsOut(int nodes, double prob) { final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); final EdgeStore edgeStore = graph.getStore().getEdgeStore(); final List nodeList = graph.getNodes(); - + Runnable runnable = () -> { for (Node node : nodeList) { Iterator itr = edgeStore.edgeOutIterator(node); @@ -77,7 +77,7 @@ public Runnable iterateEdgeStoreNeighborsInOut(int nodes, double prob) { final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); final EdgeStore edgeStore = graph.getStore().getEdgeStore(); final List nodeList = graph.getNodes(); - + Runnable runnable = () -> { for (Node node : nodeList) { Iterator itr = edgeStore.edgeIterator(node); @@ -96,7 +96,7 @@ public Runnable resetEdgeStore(int nodes, double prob) { final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); final EdgeStore edgeStore = graph.getStore().getEdgeStore(); final List edgeList = graph.getEdges(); - + Runnable runnable = () -> { for (Edge e : edgeList) { edgeStore.remove(e); diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java index 62d49558..5c40661d 100644 --- a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java @@ -43,8 +43,8 @@ public class KleinbergGraph extends Generator { private boolean torusBased; /** - * User defined Kleinberg Graph no*no = number of nodes local = local - * contacts Long = long range contacts + * User defined Kleinberg Graph no*no = number of nodes local = local contacts + * Long = long range contacts */ KleinbergGraph(int no, int local, int longRange) { super(); @@ -73,9 +73,10 @@ public KleinbergGraph generate() { for (int j = 0; j < n; ++j) { for (int k = i - p; k <= i + p; ++k) { for (int l = j - p; l <= j + p; ++l) { - if ((isTorusBased() || !isTorusBased() && k >= 0 && k < n && l >= 0 && l < n) - && d(i, j, k, l) <= p && nodes.get(i * n + j) != nodes.get(((k + n) % n) * n + ((l + n) % n))) { - Edge edge = factory.newEdge(nodes.get(i * n + j), nodes.get(((k + n) % n) * n + ((l + n) % n)), 0, true); + if ((isTorusBased() || !isTorusBased() && k >= 0 && k < n && l >= 0 && l < n) && d(i, j, k, l) <= p && nodes + .get(i * n + j) != nodes.get(((k + n) % n) * n + ((l + n) % n))) { + Edge edge = factory.newEdge(nodes.get(i * n + j), nodes + .get(((k + n) % n) * n + ((l + n) % n)), 0, true); Object id = edge.getId(); if (id instanceof Number) { edges.add(edge); diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java index 9d2e536e..4eb1f5ab 100644 --- a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java @@ -40,16 +40,16 @@ public RandomGraph(int n, double p) { public RandomGraph(int n, double p, Configuration config) { super(config); numberOfNodes = n; - numberOfEdges = (int)(n*(n-1)*p); + numberOfEdges = (int) (n * (n - 1) * p); wiringProbability = p; } - + public RandomGraph(int nodes, int edges) { this(nodes, edges, new Configuration()); } public RandomGraph(int nodes, int edges, Configuration confi) { - this(nodes, ((double)edges)/(nodes*(nodes-1)), confi); + this(nodes, ((double) edges) / (nodes * (nodes - 1)), confi); } @Override diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java b/store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java index e47c5f2e..077d541a 100644 --- a/store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java +++ b/store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java @@ -23,8 +23,9 @@ import java.util.logging.Logger; /** - * Lightweight CPU and memory benchmarking utility.

Inspired from nanobench - * (http://code.google.com/p/nanobench/) + * Lightweight CPU and memory benchmarking utility. + *

+ * Inspired from nanobench (http://code.google.com/p/nanobench/) * * @author mbastian */ @@ -33,6 +34,7 @@ public class NanoBench { public static NanoBench create() { return new NanoBench(); } + private static final Logger logger = Logger.getLogger(NanoBench.class.getSimpleName()); private int numberOfMeasurement = 50; private int numberOfWarmUp = 0; @@ -91,6 +93,7 @@ public void measure(String label, Runnable task) { logger.log(Level.SEVERE, null, ex); } } + static int[] arrayStress = new int[10000]; private void stress() { @@ -107,14 +110,16 @@ private void stress() { private void doMeasure(String label, Runnable task) { for (int i = 0; i < this.numberOfMeasurement; i++) { - TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState(label, i, this.numberOfMeasurement), task, listeners); + TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState(label, i, this.numberOfMeasurement), task, + listeners); tmp.run(); } } private void doWarmup(Runnable task) { for (int i = 0; i < this.numberOfWarmUp; i++) { - TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState("_warmup_", i, this.numberOfWarmUp), task, listeners); + TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState("_warmup_", i, this.numberOfWarmUp), task, + listeners); tmp.run(); } } @@ -225,11 +230,11 @@ public int compareTo(MeasureState another) { /** * CPU time listener to calculate the average time spent in a measurement. - *

The listener is called at the end of each measurement and collect the - * time spent from the - * MeasureState instance. At the last measurement it shows the - * average time spent, the total time and the number of measurement per - * seconds. + *

+ * The listener is called at the end of each measurement and collect the time + * spent from the MeasureState instance. At the last measurement it + * shows the average time spent, the total time and the number of measurement + * per seconds. */ private static class CPUMeasure implements MeasureListener { @@ -257,15 +262,11 @@ private void outputMeasureInfo(MeasureState state) { long total = timeUsed; StringBuilder sb = new StringBuilder("\n"); - sb.append(state.getLabel()).append("\t").append("avg: ").append( - decimalFormat.format(total / state.getMeasurements() / 1000000.0)) - .append(" ms\t").append("total: ").append( - integerFormat.format(total / 1000000000.0)).append(" s\t").append( - " tps: ").append( - integerFormat.format(state.getMeasurements() - / (total / BY_SECONDS))).append("\t") - .append("running: ").append(count) - .append(" times"); + sb.append(state.getLabel()).append("\t").append("avg: ") + .append(decimalFormat.format(total / state.getMeasurements() / 1000000.0)).append(" ms\t") + .append("total: ").append(integerFormat.format(total / 1000000000.0)).append(" s\t") + .append(" tps: ").append(integerFormat.format(state.getMeasurements() / (total / BY_SECONDS))) + .append("\t").append("running: ").append(count).append(" times"); count = 0; timeUsed = 0; if (!state.getLabel().equals("_warmup_")) { @@ -280,10 +281,11 @@ private boolean isEnd(MeasureState state) { } /** - * Memory usage listener to calculate the average memory usage.

The - * listener is called after each measurement and perform a full GC and - * calculate free memory. At the last measurement it shows the average - * memory usage. + * Memory usage listener to calculate the average memory usage. + *

+ * The listener is called after each measurement and perform a full GC and + * calculate free memory. At the last measurement it shows the average memory + * usage. */ private static class MemoryUsage implements MeasureListener { @@ -309,8 +311,7 @@ private void outputMeasureInfo(MeasureState state) { if (isEnd(state)) { StringBuilder sb = new StringBuilder("\n"); sb.append("memory-usage: ").append(state.getLabel()).append("\t") - .append(format((memoryUsed / count) / (1024.0 * 1024.0))).append( - " Mb\n"); + .append(format((memoryUsed / count) / (1024.0 * 1024.0))).append(" Mb\n"); count = 0; memoryUsed = 0; @@ -347,8 +348,7 @@ public static void restoreJvm() { long memUsedNow = memoryUsed(); // break early if have no more finalization and get constant mem used if ((ManagementFactory.getMemoryMXBean() - .getObjectPendingFinalizationCount() == 0) - && (memUsedNow >= memUsedPrev)) { + .getObjectPendingFinalizationCount() == 0) && (memUsedNow >= memUsedPrev)) { break; } else { memUsedPrev = memUsedNow; diff --git a/store/src/main/java/org/gephi/graph/api/package.html b/store/src/main/java/org/gephi/graph/api/package.html index 2b0d4e8f..172830cd 100644 --- a/store/src/main/java/org/gephi/graph/api/package.html +++ b/store/src/main/java/org/gephi/graph/api/package.html @@ -1,5 +1,5 @@ - - + + Complete API description, where GraphModel diff --git a/store/src/main/java/org/gephi/graph/api/types/package.html b/store/src/main/java/org/gephi/graph/api/types/package.html index 5c4e1bfd..dba9d896 100644 --- a/store/src/main/java/org/gephi/graph/api/types/package.html +++ b/store/src/main/java/org/gephi/graph/api/types/package.html @@ -1,5 +1,5 @@ - - + + Custom types the API supports, in addition of primitive and arrays. diff --git a/store/src/main/java/org/gephi/graph/spi/package.html b/store/src/main/java/org/gephi/graph/spi/package.html index be31cfec..b65b2e11 100644 --- a/store/src/main/java/org/gephi/graph/spi/package.html +++ b/store/src/main/java/org/gephi/graph/spi/package.html @@ -1,5 +1,5 @@ - - + + SPI interfaces clients can implement to extend the API. From 752e8514f9b83f76b3f591ecf7350b8736d8621d Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 16:09:17 +0100 Subject: [PATCH 026/271] Move all code to top-level folder --- store/LICENSE.txt => LICENSE.txt | 0 ...rmatter-config.xml => formatter-config.xml | 0 store/pom.xml => pom.xml | 0 .../graph/benchmark/ControlBenchmarkTest.java | 0 .../benchmark/EdgeStoreBenchmarkTest.java | 0 .../benchmark/NodeStoreBenchmarkTest.java | 0 .../benchmarks/DataStructureBenchmark.java | 0 .../benchmarks/EdgeStoreBenchmark.java | 0 .../graph/benchmark/benchmarks/Generator.java | 0 .../benchmark/benchmarks/KleinbergGraph.java | 0 .../benchmarks/LockingBenchmark.java | 0 .../benchmarks/NodeStoreBenchmark.java | 0 .../benchmark/benchmarks/RandomGraph.java | 0 .../graph/benchmark/nanobench/NanoBench.java | 0 .../graph/benchmark/util/ReporterHandler.java | 0 .../org/gephi/graph/api/AttributeUtils.java | 0 .../main/java/org/gephi/graph/api/Column.java | 0 .../java/org/gephi/graph/api/ColumnDiff.java | 0 .../org/gephi/graph/api/ColumnIterable.java | 0 .../org/gephi/graph/api/ColumnObserver.java | 0 .../org/gephi/graph/api/Configuration.java | 0 .../org/gephi/graph/api/DirectedGraph.java | 0 .../org/gephi/graph/api/DirectedSubgraph.java | 0 .../main/java/org/gephi/graph/api/Edge.java | 0 .../org/gephi/graph/api/EdgeIterable.java | 0 .../org/gephi/graph/api/EdgeProperties.java | 0 .../java/org/gephi/graph/api/Element.java | 0 .../org/gephi/graph/api/ElementIterable.java | 0 .../gephi/graph/api/ElementProperties.java | 0 .../java/org/gephi/graph/api/Estimator.java | 0 .../main/java/org/gephi/graph/api/Graph.java | 0 .../java/org/gephi/graph/api/GraphBridge.java | 0 .../java/org/gephi/graph/api/GraphDiff.java | 0 .../org/gephi/graph/api/GraphFactory.java | 0 .../java/org/gephi/graph/api/GraphModel.java | 0 .../org/gephi/graph/api/GraphObserver.java | 0 .../java/org/gephi/graph/api/GraphView.java | 0 .../main/java/org/gephi/graph/api/Index.java | 0 .../java/org/gephi/graph/api/Interval.java | 0 .../main/java/org/gephi/graph/api/Node.java | 0 .../org/gephi/graph/api/NodeIterable.java | 0 .../org/gephi/graph/api/NodeProperties.java | 0 .../main/java/org/gephi/graph/api/Origin.java | 0 .../main/java/org/gephi/graph/api/Rect2D.java | 0 .../org/gephi/graph/api/SpatialIndex.java | 0 .../java/org/gephi/graph/api/Subgraph.java | 0 .../main/java/org/gephi/graph/api/Table.java | 0 .../java/org/gephi/graph/api/TableDiff.java | 0 .../org/gephi/graph/api/TableObserver.java | 0 .../org/gephi/graph/api/TextProperties.java | 0 .../java/org/gephi/graph/api/TimeFormat.java | 0 .../java/org/gephi/graph/api/TimeIndex.java | 0 .../gephi/graph/api/TimeRepresentation.java | 0 .../org/gephi/graph/api/UndirectedGraph.java | 0 .../gephi/graph/api/UndirectedSubgraph.java | 0 .../java/org/gephi/graph/api/package.html | 0 .../graph/api/types/IntervalBooleanMap.java | 0 .../graph/api/types/IntervalByteMap.java | 0 .../graph/api/types/IntervalCharMap.java | 0 .../graph/api/types/IntervalDoubleMap.java | 0 .../graph/api/types/IntervalFloatMap.java | 0 .../graph/api/types/IntervalIntegerMap.java | 0 .../graph/api/types/IntervalLongMap.java | 0 .../gephi/graph/api/types/IntervalMap.java | 0 .../gephi/graph/api/types/IntervalSet.java | 0 .../graph/api/types/IntervalShortMap.java | 0 .../graph/api/types/IntervalStringMap.java | 0 .../org/gephi/graph/api/types/TimeMap.java | 0 .../org/gephi/graph/api/types/TimeSet.java | 0 .../graph/api/types/TimestampBooleanMap.java | 0 .../graph/api/types/TimestampByteMap.java | 0 .../graph/api/types/TimestampCharMap.java | 0 .../graph/api/types/TimestampDoubleMap.java | 0 .../graph/api/types/TimestampFloatMap.java | 0 .../graph/api/types/TimestampIntegerMap.java | 0 .../graph/api/types/TimestampLongMap.java | 0 .../gephi/graph/api/types/TimestampMap.java | 0 .../gephi/graph/api/types/TimestampSet.java | 0 .../graph/api/types/TimestampShortMap.java | 0 .../graph/api/types/TimestampStringMap.java | 0 .../org/gephi/graph/api/types/package.html | 0 .../org/gephi/graph/impl/ArraysParser.java | 0 .../java/org/gephi/graph/impl/ColumnImpl.java | 0 .../gephi/graph/impl/ColumnObserverImpl.java | 0 .../org/gephi/graph/impl/ColumnStore.java | 0 .../org/gephi/graph/impl/ColumnVersion.java | 0 .../java/org/gephi/graph/impl/EdgeImpl.java | 0 .../gephi/graph/impl/EdgeIterableWrapper.java | 0 .../java/org/gephi/graph/impl/EdgeStore.java | 0 .../org/gephi/graph/impl/EdgeTypeStore.java | 0 .../org/gephi/graph/impl/ElementImpl.java | 0 .../graph/impl/ElementIterableWrapper.java | 0 .../graph/impl/FormattingAndParsingUtils.java | 0 .../gephi/graph/impl/GraphAttributesImpl.java | 0 .../org/gephi/graph/impl/GraphBridgeImpl.java | 0 .../gephi/graph/impl/GraphFactoryImpl.java | 0 .../java/org/gephi/graph/impl/GraphLock.java | 0 .../org/gephi/graph/impl/GraphModelImpl.java | 0 .../gephi/graph/impl/GraphObserverImpl.java | 0 .../java/org/gephi/graph/impl/GraphStore.java | 0 .../graph/impl/GraphStoreConfiguration.java | 0 .../org/gephi/graph/impl/GraphVersion.java | 0 .../gephi/graph/impl/GraphViewDecorator.java | 0 .../org/gephi/graph/impl/GraphViewImpl.java | 0 .../org/gephi/graph/impl/GraphViewStore.java | 0 .../java/org/gephi/graph/impl/IndexImpl.java | 0 .../java/org/gephi/graph/impl/IndexStore.java | 0 .../gephi/graph/impl/Interval2IntTreeMap.java | 0 .../gephi/graph/impl/IntervalIndexImpl.java | 0 .../gephi/graph/impl/IntervalIndexStore.java | 0 .../org/gephi/graph/impl/IntervalsParser.java | 0 .../java/org/gephi/graph/impl/NodeImpl.java | 0 .../gephi/graph/impl/NodeIterableWrapper.java | 0 .../java/org/gephi/graph/impl/NodeStore.java | 0 .../org/gephi/graph/impl/NodesQuadTree.java | 0 .../org/gephi/graph/impl/Serialization.java | 0 .../gephi/graph/impl/SpatialIndexImpl.java | 0 .../gephi/graph/impl/SpatialNodeDataImpl.java | 0 .../java/org/gephi/graph/impl/TableImpl.java | 0 .../java/org/gephi/graph/impl/TableLock.java | 0 .../gephi/graph/impl/TableObserverImpl.java | 0 .../gephi/graph/impl/TextPropertiesImpl.java | 0 .../graph/impl/TimeAttributeIterable.java | 0 .../org/gephi/graph/impl/TimeIndexImpl.java | 0 .../org/gephi/graph/impl/TimeIndexStore.java | 0 .../java/org/gephi/graph/impl/TimeStore.java | 0 .../gephi/graph/impl/TimestampIndexImpl.java | 0 .../gephi/graph/impl/TimestampIndexStore.java | 0 .../gephi/graph/impl/TimestampsParser.java | 0 .../gephi/graph/impl/UndirectedDecorator.java | 0 .../graph/impl/utils/DataInputOutput.java | 0 .../gephi/graph/impl/utils/LongPacker.java | 0 .../gephi/graph/impl/utils/MapDeepEquals.java | 0 .../java/org/gephi/graph/spi/LayoutData.java | 0 .../java/org/gephi/graph/spi/package.html | 0 .../graph/api/types/IntervalMapTest.java | 0 .../graph/api/types/IntervalSetTest.java | 0 .../graph/api/types/TimestampMapTest.java | 0 .../graph/api/types/TimestampSetTest.java | 0 .../gephi/graph/impl/ArraysParserTest.java | 0 .../gephi/graph/impl/AttributeUtilsTest.java | 0 .../org/gephi/graph/impl/BasicGraphStore.java | 0 .../org/gephi/graph/impl/ColumnImplTest.java | 0 .../gephi/graph/impl/ColumnObserverTest.java | 0 .../org/gephi/graph/impl/ColumnStoreTest.java | 0 .../gephi/graph/impl/ColumnVersionTest.java | 0 .../gephi/graph/impl/ConfigurationTest.java | 0 .../org/gephi/graph/impl/EdgeImplTest.java | 0 .../org/gephi/graph/impl/EdgeStoreTest.java | 0 .../gephi/graph/impl/EdgeTypeStoreTest.java | 0 .../org/gephi/graph/impl/ElementImplTest.java | 0 .../graph/impl/ElementPropertiesTest.java | 0 .../gephi/graph/impl/EmptyIterableTest.java | 0 .../org/gephi/graph/impl/EstimatorTest.java | 0 .../gephi/graph/impl/GraphAttributesTest.java | 0 .../org/gephi/graph/impl/GraphBridgeTest.java | 0 .../gephi/graph/impl/GraphFactoryTest.java | 0 .../org/gephi/graph/impl/GraphGenerator.java | 0 .../org/gephi/graph/impl/GraphLockTest.java | 0 .../org/gephi/graph/impl/GraphModelTest.java | 0 .../gephi/graph/impl/GraphObserverTest.java | 0 .../org/gephi/graph/impl/GraphStoreTest.java | 0 .../gephi/graph/impl/GraphVersionTest.java | 0 .../graph/impl/GraphViewDecoratorTest.java | 0 .../gephi/graph/impl/GraphViewImplTest.java | 0 .../gephi/graph/impl/GraphViewStoreTest.java | 0 .../org/gephi/graph/impl/IndexImplTest.java | 0 .../org/gephi/graph/impl/IndexStoreTest.java | 0 .../graph/impl/IntervalIndexImplTest.java | 0 .../graph/impl/IntervalIndexStoreTest.java | 0 .../org/gephi/graph/impl/IntervalTest.java | 0 .../gephi/graph/impl/IntervalTreeMapTest.java | 0 .../gephi/graph/impl/IntervalsParserTest.java | 0 .../org/gephi/graph/impl/LongPackerTest.java | 0 .../org/gephi/graph/impl/NodeStoreTest.java | 0 .../gephi/graph/impl/NodesQuadTreeTest.java | 0 .../org/gephi/graph/impl/NumberGenerator.java | 0 .../gephi/graph/impl/SerializationTest.java | 0 .../graph/impl/SpatialIndexImplTest.java | 0 .../org/gephi/graph/impl/TableImplTest.java | 0 .../gephi/graph/impl/TableObserverTest.java | 0 .../org/gephi/graph/impl/TimeStoreTest.java | 0 .../graph/impl/TimestampIndexImplTest.java | 0 .../graph/impl/TimestampIndexStoreTest.java | 0 .../graph/impl/TimestampsParserTest.java | 0 .../graph/impl/UndirectedDecoratorTest.java | 0 store-benchmark/pom.xml | 79 --- .../benchmark/DataStructureBenchmark.java | 570 ------------------ .../graph/benchmark/EdgeStoreBenchmark.java | 110 ---- .../org/gephi/graph/benchmark/Generator.java | 61 -- .../gephi/graph/benchmark/KleinbergGraph.java | 187 ------ .../graph/benchmark/LockingBenchmark.java | 270 --------- .../graph/benchmark/NodeStoreBenchmark.java | 84 --- .../gephi/graph/benchmark/RandomGraph.java | 85 --- .../java/org/gephi/nanobench/NanoBench.java | 369 ------------ .../graph/benchmark/ControlBenchmarkTest.java | 54 -- .../benchmark/EdgeStoreBenchmarkTest.java | 82 --- .../benchmark/NodeStoreBenchmarkTest.java | 46 -- .../graph/benchmark/util/ReporterHandler.java | 50 -- 199 files changed, 2047 deletions(-) rename store/LICENSE.txt => LICENSE.txt (100%) rename store/formatter-config.xml => formatter-config.xml (100%) rename store/pom.xml => pom.xml (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java (100%) rename {store/src => src}/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java (100%) rename {store-benchmark/src/test => src/benchmark}/java/org/gephi/graph/benchmark/util/ReporterHandler.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/AttributeUtils.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Column.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/ColumnDiff.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/ColumnIterable.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/ColumnObserver.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Configuration.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/DirectedGraph.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/DirectedSubgraph.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Edge.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/EdgeIterable.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/EdgeProperties.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Element.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/ElementIterable.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/ElementProperties.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Estimator.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Graph.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/GraphBridge.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/GraphDiff.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/GraphFactory.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/GraphModel.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/GraphObserver.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/GraphView.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Index.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Interval.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Node.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/NodeIterable.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/NodeProperties.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Origin.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Rect2D.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/SpatialIndex.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Subgraph.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/Table.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/TableDiff.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/TableObserver.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/TextProperties.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/TimeFormat.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/TimeIndex.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/TimeRepresentation.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/UndirectedGraph.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/UndirectedSubgraph.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/package.html (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalByteMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalCharMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalFloatMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalLongMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalSet.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalShortMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/IntervalStringMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimeMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimeSet.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampByteMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampCharMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampFloatMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampLongMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampSet.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampShortMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/TimestampStringMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/api/types/package.html (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/ArraysParser.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/ColumnImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/ColumnObserverImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/ColumnStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/ColumnVersion.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/EdgeImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/EdgeStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/EdgeTypeStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/ElementImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/ElementIterableWrapper.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphAttributesImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphBridgeImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphFactoryImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphLock.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphModelImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphObserverImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphVersion.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphViewDecorator.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphViewImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/GraphViewStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/IndexImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/IndexStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/IntervalIndexImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/IntervalIndexStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/IntervalsParser.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/NodeImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/NodeIterableWrapper.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/NodeStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/NodesQuadTree.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/Serialization.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/SpatialIndexImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TableImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TableLock.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TableObserverImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TextPropertiesImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TimeAttributeIterable.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TimeIndexImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TimeIndexStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TimeStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TimestampIndexImpl.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TimestampIndexStore.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/TimestampsParser.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/UndirectedDecorator.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/utils/DataInputOutput.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/utils/LongPacker.java (100%) rename {store/src => src}/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java (100%) rename {store/src => src}/main/java/org/gephi/graph/spi/LayoutData.java (100%) rename {store/src => src}/main/java/org/gephi/graph/spi/package.html (100%) rename {store/src => src}/test/java/org/gephi/graph/api/types/IntervalMapTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/api/types/IntervalSetTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/api/types/TimestampMapTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/api/types/TimestampSetTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/ArraysParserTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/AttributeUtilsTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/BasicGraphStore.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/ColumnImplTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/ColumnObserverTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/ColumnStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/ColumnVersionTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/ConfigurationTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/EdgeImplTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/EdgeStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/ElementImplTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/ElementPropertiesTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/EmptyIterableTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/EstimatorTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphAttributesTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphBridgeTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphFactoryTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphGenerator.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphLockTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphModelTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphObserverTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphVersionTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphViewImplTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/GraphViewStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/IndexImplTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/IndexStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/IntervalIndexImplTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/IntervalTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/IntervalTreeMapTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/IntervalsParserTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/LongPackerTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/NodeStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/NumberGenerator.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/SerializationTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/TableImplTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/TableObserverTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/TimeStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/TimestampIndexImplTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/TimestampsParserTest.java (100%) rename {store/src => src}/test/java/org/gephi/graph/impl/UndirectedDecoratorTest.java (100%) delete mode 100644 store-benchmark/pom.xml delete mode 100644 store-benchmark/src/main/java/org/gephi/graph/benchmark/DataStructureBenchmark.java delete mode 100644 store-benchmark/src/main/java/org/gephi/graph/benchmark/EdgeStoreBenchmark.java delete mode 100644 store-benchmark/src/main/java/org/gephi/graph/benchmark/Generator.java delete mode 100644 store-benchmark/src/main/java/org/gephi/graph/benchmark/KleinbergGraph.java delete mode 100644 store-benchmark/src/main/java/org/gephi/graph/benchmark/LockingBenchmark.java delete mode 100644 store-benchmark/src/main/java/org/gephi/graph/benchmark/NodeStoreBenchmark.java delete mode 100644 store-benchmark/src/main/java/org/gephi/graph/benchmark/RandomGraph.java delete mode 100644 store-benchmark/src/main/java/org/gephi/nanobench/NanoBench.java delete mode 100644 store-benchmark/src/test/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java delete mode 100644 store-benchmark/src/test/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java delete mode 100644 store-benchmark/src/test/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java delete mode 100644 store/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java diff --git a/store/LICENSE.txt b/LICENSE.txt similarity index 100% rename from store/LICENSE.txt rename to LICENSE.txt diff --git a/store/formatter-config.xml b/formatter-config.xml similarity index 100% rename from store/formatter-config.xml rename to formatter-config.xml diff --git a/store/pom.xml b/pom.xml similarity index 100% rename from store/pom.xml rename to pom.xml diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java b/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java rename to src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java b/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java rename to src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java b/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java rename to src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java rename to src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java rename to src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java rename to src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java rename to src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java rename to src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java rename to src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java rename to src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java b/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java similarity index 100% rename from store/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java rename to src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java diff --git a/store-benchmark/src/test/java/org/gephi/graph/benchmark/util/ReporterHandler.java b/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java similarity index 100% rename from store-benchmark/src/test/java/org/gephi/graph/benchmark/util/ReporterHandler.java rename to src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java diff --git a/store/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/AttributeUtils.java rename to src/main/java/org/gephi/graph/api/AttributeUtils.java diff --git a/store/src/main/java/org/gephi/graph/api/Column.java b/src/main/java/org/gephi/graph/api/Column.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Column.java rename to src/main/java/org/gephi/graph/api/Column.java diff --git a/store/src/main/java/org/gephi/graph/api/ColumnDiff.java b/src/main/java/org/gephi/graph/api/ColumnDiff.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/ColumnDiff.java rename to src/main/java/org/gephi/graph/api/ColumnDiff.java diff --git a/store/src/main/java/org/gephi/graph/api/ColumnIterable.java b/src/main/java/org/gephi/graph/api/ColumnIterable.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/ColumnIterable.java rename to src/main/java/org/gephi/graph/api/ColumnIterable.java diff --git a/store/src/main/java/org/gephi/graph/api/ColumnObserver.java b/src/main/java/org/gephi/graph/api/ColumnObserver.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/ColumnObserver.java rename to src/main/java/org/gephi/graph/api/ColumnObserver.java diff --git a/store/src/main/java/org/gephi/graph/api/Configuration.java b/src/main/java/org/gephi/graph/api/Configuration.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Configuration.java rename to src/main/java/org/gephi/graph/api/Configuration.java diff --git a/store/src/main/java/org/gephi/graph/api/DirectedGraph.java b/src/main/java/org/gephi/graph/api/DirectedGraph.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/DirectedGraph.java rename to src/main/java/org/gephi/graph/api/DirectedGraph.java diff --git a/store/src/main/java/org/gephi/graph/api/DirectedSubgraph.java b/src/main/java/org/gephi/graph/api/DirectedSubgraph.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/DirectedSubgraph.java rename to src/main/java/org/gephi/graph/api/DirectedSubgraph.java diff --git a/store/src/main/java/org/gephi/graph/api/Edge.java b/src/main/java/org/gephi/graph/api/Edge.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Edge.java rename to src/main/java/org/gephi/graph/api/Edge.java diff --git a/store/src/main/java/org/gephi/graph/api/EdgeIterable.java b/src/main/java/org/gephi/graph/api/EdgeIterable.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/EdgeIterable.java rename to src/main/java/org/gephi/graph/api/EdgeIterable.java diff --git a/store/src/main/java/org/gephi/graph/api/EdgeProperties.java b/src/main/java/org/gephi/graph/api/EdgeProperties.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/EdgeProperties.java rename to src/main/java/org/gephi/graph/api/EdgeProperties.java diff --git a/store/src/main/java/org/gephi/graph/api/Element.java b/src/main/java/org/gephi/graph/api/Element.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Element.java rename to src/main/java/org/gephi/graph/api/Element.java diff --git a/store/src/main/java/org/gephi/graph/api/ElementIterable.java b/src/main/java/org/gephi/graph/api/ElementIterable.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/ElementIterable.java rename to src/main/java/org/gephi/graph/api/ElementIterable.java diff --git a/store/src/main/java/org/gephi/graph/api/ElementProperties.java b/src/main/java/org/gephi/graph/api/ElementProperties.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/ElementProperties.java rename to src/main/java/org/gephi/graph/api/ElementProperties.java diff --git a/store/src/main/java/org/gephi/graph/api/Estimator.java b/src/main/java/org/gephi/graph/api/Estimator.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Estimator.java rename to src/main/java/org/gephi/graph/api/Estimator.java diff --git a/store/src/main/java/org/gephi/graph/api/Graph.java b/src/main/java/org/gephi/graph/api/Graph.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Graph.java rename to src/main/java/org/gephi/graph/api/Graph.java diff --git a/store/src/main/java/org/gephi/graph/api/GraphBridge.java b/src/main/java/org/gephi/graph/api/GraphBridge.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/GraphBridge.java rename to src/main/java/org/gephi/graph/api/GraphBridge.java diff --git a/store/src/main/java/org/gephi/graph/api/GraphDiff.java b/src/main/java/org/gephi/graph/api/GraphDiff.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/GraphDiff.java rename to src/main/java/org/gephi/graph/api/GraphDiff.java diff --git a/store/src/main/java/org/gephi/graph/api/GraphFactory.java b/src/main/java/org/gephi/graph/api/GraphFactory.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/GraphFactory.java rename to src/main/java/org/gephi/graph/api/GraphFactory.java diff --git a/store/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/GraphModel.java rename to src/main/java/org/gephi/graph/api/GraphModel.java diff --git a/store/src/main/java/org/gephi/graph/api/GraphObserver.java b/src/main/java/org/gephi/graph/api/GraphObserver.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/GraphObserver.java rename to src/main/java/org/gephi/graph/api/GraphObserver.java diff --git a/store/src/main/java/org/gephi/graph/api/GraphView.java b/src/main/java/org/gephi/graph/api/GraphView.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/GraphView.java rename to src/main/java/org/gephi/graph/api/GraphView.java diff --git a/store/src/main/java/org/gephi/graph/api/Index.java b/src/main/java/org/gephi/graph/api/Index.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Index.java rename to src/main/java/org/gephi/graph/api/Index.java diff --git a/store/src/main/java/org/gephi/graph/api/Interval.java b/src/main/java/org/gephi/graph/api/Interval.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Interval.java rename to src/main/java/org/gephi/graph/api/Interval.java diff --git a/store/src/main/java/org/gephi/graph/api/Node.java b/src/main/java/org/gephi/graph/api/Node.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Node.java rename to src/main/java/org/gephi/graph/api/Node.java diff --git a/store/src/main/java/org/gephi/graph/api/NodeIterable.java b/src/main/java/org/gephi/graph/api/NodeIterable.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/NodeIterable.java rename to src/main/java/org/gephi/graph/api/NodeIterable.java diff --git a/store/src/main/java/org/gephi/graph/api/NodeProperties.java b/src/main/java/org/gephi/graph/api/NodeProperties.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/NodeProperties.java rename to src/main/java/org/gephi/graph/api/NodeProperties.java diff --git a/store/src/main/java/org/gephi/graph/api/Origin.java b/src/main/java/org/gephi/graph/api/Origin.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Origin.java rename to src/main/java/org/gephi/graph/api/Origin.java diff --git a/store/src/main/java/org/gephi/graph/api/Rect2D.java b/src/main/java/org/gephi/graph/api/Rect2D.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Rect2D.java rename to src/main/java/org/gephi/graph/api/Rect2D.java diff --git a/store/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/SpatialIndex.java rename to src/main/java/org/gephi/graph/api/SpatialIndex.java diff --git a/store/src/main/java/org/gephi/graph/api/Subgraph.java b/src/main/java/org/gephi/graph/api/Subgraph.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Subgraph.java rename to src/main/java/org/gephi/graph/api/Subgraph.java diff --git a/store/src/main/java/org/gephi/graph/api/Table.java b/src/main/java/org/gephi/graph/api/Table.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/Table.java rename to src/main/java/org/gephi/graph/api/Table.java diff --git a/store/src/main/java/org/gephi/graph/api/TableDiff.java b/src/main/java/org/gephi/graph/api/TableDiff.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/TableDiff.java rename to src/main/java/org/gephi/graph/api/TableDiff.java diff --git a/store/src/main/java/org/gephi/graph/api/TableObserver.java b/src/main/java/org/gephi/graph/api/TableObserver.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/TableObserver.java rename to src/main/java/org/gephi/graph/api/TableObserver.java diff --git a/store/src/main/java/org/gephi/graph/api/TextProperties.java b/src/main/java/org/gephi/graph/api/TextProperties.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/TextProperties.java rename to src/main/java/org/gephi/graph/api/TextProperties.java diff --git a/store/src/main/java/org/gephi/graph/api/TimeFormat.java b/src/main/java/org/gephi/graph/api/TimeFormat.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/TimeFormat.java rename to src/main/java/org/gephi/graph/api/TimeFormat.java diff --git a/store/src/main/java/org/gephi/graph/api/TimeIndex.java b/src/main/java/org/gephi/graph/api/TimeIndex.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/TimeIndex.java rename to src/main/java/org/gephi/graph/api/TimeIndex.java diff --git a/store/src/main/java/org/gephi/graph/api/TimeRepresentation.java b/src/main/java/org/gephi/graph/api/TimeRepresentation.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/TimeRepresentation.java rename to src/main/java/org/gephi/graph/api/TimeRepresentation.java diff --git a/store/src/main/java/org/gephi/graph/api/UndirectedGraph.java b/src/main/java/org/gephi/graph/api/UndirectedGraph.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/UndirectedGraph.java rename to src/main/java/org/gephi/graph/api/UndirectedGraph.java diff --git a/store/src/main/java/org/gephi/graph/api/UndirectedSubgraph.java b/src/main/java/org/gephi/graph/api/UndirectedSubgraph.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/UndirectedSubgraph.java rename to src/main/java/org/gephi/graph/api/UndirectedSubgraph.java diff --git a/store/src/main/java/org/gephi/graph/api/package.html b/src/main/java/org/gephi/graph/api/package.html similarity index 100% rename from store/src/main/java/org/gephi/graph/api/package.html rename to src/main/java/org/gephi/graph/api/package.html diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java b/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java b/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalByteMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java b/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalCharMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java b/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java b/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java b/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java b/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalLongMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalMap.java b/src/main/java/org/gephi/graph/api/types/IntervalMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalSet.java b/src/main/java/org/gephi/graph/api/types/IntervalSet.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalSet.java rename to src/main/java/org/gephi/graph/api/types/IntervalSet.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java b/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalShortMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java b/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java rename to src/main/java/org/gephi/graph/api/types/IntervalStringMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimeMap.java b/src/main/java/org/gephi/graph/api/types/TimeMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimeMap.java rename to src/main/java/org/gephi/graph/api/types/TimeMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimeSet.java b/src/main/java/org/gephi/graph/api/types/TimeSet.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimeSet.java rename to src/main/java/org/gephi/graph/api/types/TimeSet.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java b/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java b/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampByteMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java b/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampCharMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java b/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java b/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java b/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java b/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampLongMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampMap.java b/src/main/java/org/gephi/graph/api/types/TimestampMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampSet.java b/src/main/java/org/gephi/graph/api/types/TimestampSet.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampSet.java rename to src/main/java/org/gephi/graph/api/types/TimestampSet.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java b/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampShortMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java b/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java rename to src/main/java/org/gephi/graph/api/types/TimestampStringMap.java diff --git a/store/src/main/java/org/gephi/graph/api/types/package.html b/src/main/java/org/gephi/graph/api/types/package.html similarity index 100% rename from store/src/main/java/org/gephi/graph/api/types/package.html rename to src/main/java/org/gephi/graph/api/types/package.html diff --git a/store/src/main/java/org/gephi/graph/impl/ArraysParser.java b/src/main/java/org/gephi/graph/impl/ArraysParser.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/ArraysParser.java rename to src/main/java/org/gephi/graph/impl/ArraysParser.java diff --git a/store/src/main/java/org/gephi/graph/impl/ColumnImpl.java b/src/main/java/org/gephi/graph/impl/ColumnImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/ColumnImpl.java rename to src/main/java/org/gephi/graph/impl/ColumnImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java b/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java rename to src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/ColumnStore.java rename to src/main/java/org/gephi/graph/impl/ColumnStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/ColumnVersion.java b/src/main/java/org/gephi/graph/impl/ColumnVersion.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/ColumnVersion.java rename to src/main/java/org/gephi/graph/impl/ColumnVersion.java diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/src/main/java/org/gephi/graph/impl/EdgeImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/EdgeImpl.java rename to src/main/java/org/gephi/graph/impl/EdgeImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java b/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java rename to src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/EdgeStore.java rename to src/main/java/org/gephi/graph/impl/EdgeStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java rename to src/main/java/org/gephi/graph/impl/EdgeTypeStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/ElementImpl.java rename to src/main/java/org/gephi/graph/impl/ElementImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java b/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java rename to src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java diff --git a/store/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java rename to src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java b/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java rename to src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java rename to src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java b/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java rename to src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphLock.java b/src/main/java/org/gephi/graph/impl/GraphLock.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphLock.java rename to src/main/java/org/gephi/graph/impl/GraphLock.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphModelImpl.java rename to src/main/java/org/gephi/graph/impl/GraphModelImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java b/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java rename to src/main/java/org/gephi/graph/impl/GraphObserverImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphStore.java rename to src/main/java/org/gephi/graph/impl/GraphStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java rename to src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphVersion.java b/src/main/java/org/gephi/graph/impl/GraphVersion.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphVersion.java rename to src/main/java/org/gephi/graph/impl/GraphVersion.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java rename to src/main/java/org/gephi/graph/impl/GraphViewDecorator.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphViewImpl.java rename to src/main/java/org/gephi/graph/impl/GraphViewImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/src/main/java/org/gephi/graph/impl/GraphViewStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/GraphViewStore.java rename to src/main/java/org/gephi/graph/impl/GraphViewStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/IndexImpl.java rename to src/main/java/org/gephi/graph/impl/IndexImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/IndexStore.java b/src/main/java/org/gephi/graph/impl/IndexStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/IndexStore.java rename to src/main/java/org/gephi/graph/impl/IndexStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java b/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java rename to src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java diff --git a/store/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java b/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java rename to src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java b/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java rename to src/main/java/org/gephi/graph/impl/IntervalIndexStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/IntervalsParser.java b/src/main/java/org/gephi/graph/impl/IntervalsParser.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/IntervalsParser.java rename to src/main/java/org/gephi/graph/impl/IntervalsParser.java diff --git a/store/src/main/java/org/gephi/graph/impl/NodeImpl.java b/src/main/java/org/gephi/graph/impl/NodeImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/NodeImpl.java rename to src/main/java/org/gephi/graph/impl/NodeImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java b/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java rename to src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java diff --git a/store/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/NodeStore.java rename to src/main/java/org/gephi/graph/impl/NodeStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/NodesQuadTree.java rename to src/main/java/org/gephi/graph/impl/NodesQuadTree.java diff --git a/store/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/Serialization.java rename to src/main/java/org/gephi/graph/impl/Serialization.java diff --git a/store/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java rename to src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java b/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java rename to src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TableImpl.java rename to src/main/java/org/gephi/graph/impl/TableImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/TableLock.java b/src/main/java/org/gephi/graph/impl/TableLock.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TableLock.java rename to src/main/java/org/gephi/graph/impl/TableLock.java diff --git a/store/src/main/java/org/gephi/graph/impl/TableObserverImpl.java b/src/main/java/org/gephi/graph/impl/TableObserverImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TableObserverImpl.java rename to src/main/java/org/gephi/graph/impl/TableObserverImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/TextPropertiesImpl.java b/src/main/java/org/gephi/graph/impl/TextPropertiesImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TextPropertiesImpl.java rename to src/main/java/org/gephi/graph/impl/TextPropertiesImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/TimeAttributeIterable.java b/src/main/java/org/gephi/graph/impl/TimeAttributeIterable.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TimeAttributeIterable.java rename to src/main/java/org/gephi/graph/impl/TimeAttributeIterable.java diff --git a/store/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java rename to src/main/java/org/gephi/graph/impl/TimeIndexImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/TimeIndexStore.java b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TimeIndexStore.java rename to src/main/java/org/gephi/graph/impl/TimeIndexStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/TimeStore.java b/src/main/java/org/gephi/graph/impl/TimeStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TimeStore.java rename to src/main/java/org/gephi/graph/impl/TimeStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java b/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java rename to src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java diff --git a/store/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java b/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java rename to src/main/java/org/gephi/graph/impl/TimestampIndexStore.java diff --git a/store/src/main/java/org/gephi/graph/impl/TimestampsParser.java b/src/main/java/org/gephi/graph/impl/TimestampsParser.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/TimestampsParser.java rename to src/main/java/org/gephi/graph/impl/TimestampsParser.java diff --git a/store/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java rename to src/main/java/org/gephi/graph/impl/UndirectedDecorator.java diff --git a/store/src/main/java/org/gephi/graph/impl/utils/DataInputOutput.java b/src/main/java/org/gephi/graph/impl/utils/DataInputOutput.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/utils/DataInputOutput.java rename to src/main/java/org/gephi/graph/impl/utils/DataInputOutput.java diff --git a/store/src/main/java/org/gephi/graph/impl/utils/LongPacker.java b/src/main/java/org/gephi/graph/impl/utils/LongPacker.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/utils/LongPacker.java rename to src/main/java/org/gephi/graph/impl/utils/LongPacker.java diff --git a/store/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java b/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java similarity index 100% rename from store/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java rename to src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java diff --git a/store/src/main/java/org/gephi/graph/spi/LayoutData.java b/src/main/java/org/gephi/graph/spi/LayoutData.java similarity index 100% rename from store/src/main/java/org/gephi/graph/spi/LayoutData.java rename to src/main/java/org/gephi/graph/spi/LayoutData.java diff --git a/store/src/main/java/org/gephi/graph/spi/package.html b/src/main/java/org/gephi/graph/spi/package.html similarity index 100% rename from store/src/main/java/org/gephi/graph/spi/package.html rename to src/main/java/org/gephi/graph/spi/package.html diff --git a/store/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java b/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java rename to src/test/java/org/gephi/graph/api/types/IntervalMapTest.java diff --git a/store/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java b/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java rename to src/test/java/org/gephi/graph/api/types/IntervalSetTest.java diff --git a/store/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java b/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java rename to src/test/java/org/gephi/graph/api/types/TimestampMapTest.java diff --git a/store/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java b/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java rename to src/test/java/org/gephi/graph/api/types/TimestampSetTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/ArraysParserTest.java b/src/test/java/org/gephi/graph/impl/ArraysParserTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/ArraysParserTest.java rename to src/test/java/org/gephi/graph/impl/ArraysParserTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java rename to src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/BasicGraphStore.java rename to src/test/java/org/gephi/graph/impl/BasicGraphStore.java diff --git a/store/src/test/java/org/gephi/graph/impl/ColumnImplTest.java b/src/test/java/org/gephi/graph/impl/ColumnImplTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/ColumnImplTest.java rename to src/test/java/org/gephi/graph/impl/ColumnImplTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/ColumnObserverTest.java b/src/test/java/org/gephi/graph/impl/ColumnObserverTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/ColumnObserverTest.java rename to src/test/java/org/gephi/graph/impl/ColumnObserverTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java b/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java rename to src/test/java/org/gephi/graph/impl/ColumnStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/ColumnVersionTest.java b/src/test/java/org/gephi/graph/impl/ColumnVersionTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/ColumnVersionTest.java rename to src/test/java/org/gephi/graph/impl/ColumnVersionTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/ConfigurationTest.java b/src/test/java/org/gephi/graph/impl/ConfigurationTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/ConfigurationTest.java rename to src/test/java/org/gephi/graph/impl/ConfigurationTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java b/src/test/java/org/gephi/graph/impl/EdgeImplTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/EdgeImplTest.java rename to src/test/java/org/gephi/graph/impl/EdgeImplTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java rename to src/test/java/org/gephi/graph/impl/EdgeStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java rename to src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/ElementImplTest.java rename to src/test/java/org/gephi/graph/impl/ElementImplTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/ElementPropertiesTest.java b/src/test/java/org/gephi/graph/impl/ElementPropertiesTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/ElementPropertiesTest.java rename to src/test/java/org/gephi/graph/impl/ElementPropertiesTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/EmptyIterableTest.java b/src/test/java/org/gephi/graph/impl/EmptyIterableTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/EmptyIterableTest.java rename to src/test/java/org/gephi/graph/impl/EmptyIterableTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/EstimatorTest.java b/src/test/java/org/gephi/graph/impl/EstimatorTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/EstimatorTest.java rename to src/test/java/org/gephi/graph/impl/EstimatorTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphAttributesTest.java b/src/test/java/org/gephi/graph/impl/GraphAttributesTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphAttributesTest.java rename to src/test/java/org/gephi/graph/impl/GraphAttributesTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java rename to src/test/java/org/gephi/graph/impl/GraphBridgeTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphFactoryTest.java b/src/test/java/org/gephi/graph/impl/GraphFactoryTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphFactoryTest.java rename to src/test/java/org/gephi/graph/impl/GraphFactoryTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/src/test/java/org/gephi/graph/impl/GraphGenerator.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphGenerator.java rename to src/test/java/org/gephi/graph/impl/GraphGenerator.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphLockTest.java b/src/test/java/org/gephi/graph/impl/GraphLockTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphLockTest.java rename to src/test/java/org/gephi/graph/impl/GraphLockTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/src/test/java/org/gephi/graph/impl/GraphModelTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphModelTest.java rename to src/test/java/org/gephi/graph/impl/GraphModelTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphObserverTest.java b/src/test/java/org/gephi/graph/impl/GraphObserverTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphObserverTest.java rename to src/test/java/org/gephi/graph/impl/GraphObserverTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphStoreTest.java rename to src/test/java/org/gephi/graph/impl/GraphStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphVersionTest.java b/src/test/java/org/gephi/graph/impl/GraphVersionTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphVersionTest.java rename to src/test/java/org/gephi/graph/impl/GraphVersionTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java rename to src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java rename to src/test/java/org/gephi/graph/impl/GraphViewImplTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java rename to src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/IndexImplTest.java b/src/test/java/org/gephi/graph/impl/IndexImplTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/IndexImplTest.java rename to src/test/java/org/gephi/graph/impl/IndexImplTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/IndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/IndexStoreTest.java rename to src/test/java/org/gephi/graph/impl/IndexStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/IntervalIndexImplTest.java b/src/test/java/org/gephi/graph/impl/IntervalIndexImplTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/IntervalIndexImplTest.java rename to src/test/java/org/gephi/graph/impl/IntervalIndexImplTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java rename to src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/IntervalTest.java b/src/test/java/org/gephi/graph/impl/IntervalTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/IntervalTest.java rename to src/test/java/org/gephi/graph/impl/IntervalTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/IntervalTreeMapTest.java b/src/test/java/org/gephi/graph/impl/IntervalTreeMapTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/IntervalTreeMapTest.java rename to src/test/java/org/gephi/graph/impl/IntervalTreeMapTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java b/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java rename to src/test/java/org/gephi/graph/impl/IntervalsParserTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/LongPackerTest.java b/src/test/java/org/gephi/graph/impl/LongPackerTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/LongPackerTest.java rename to src/test/java/org/gephi/graph/impl/LongPackerTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/NodeStoreTest.java b/src/test/java/org/gephi/graph/impl/NodeStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/NodeStoreTest.java rename to src/test/java/org/gephi/graph/impl/NodeStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java b/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java rename to src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/NumberGenerator.java b/src/test/java/org/gephi/graph/impl/NumberGenerator.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/NumberGenerator.java rename to src/test/java/org/gephi/graph/impl/NumberGenerator.java diff --git a/store/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/SerializationTest.java rename to src/test/java/org/gephi/graph/impl/SerializationTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java b/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java rename to src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/TableImplTest.java b/src/test/java/org/gephi/graph/impl/TableImplTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/TableImplTest.java rename to src/test/java/org/gephi/graph/impl/TableImplTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/TableObserverTest.java b/src/test/java/org/gephi/graph/impl/TableObserverTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/TableObserverTest.java rename to src/test/java/org/gephi/graph/impl/TableObserverTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/TimeStoreTest.java b/src/test/java/org/gephi/graph/impl/TimeStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/TimeStoreTest.java rename to src/test/java/org/gephi/graph/impl/TimeStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/TimestampIndexImplTest.java b/src/test/java/org/gephi/graph/impl/TimestampIndexImplTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/TimestampIndexImplTest.java rename to src/test/java/org/gephi/graph/impl/TimestampIndexImplTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java b/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java rename to src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java b/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java rename to src/test/java/org/gephi/graph/impl/TimestampsParserTest.java diff --git a/store/src/test/java/org/gephi/graph/impl/UndirectedDecoratorTest.java b/src/test/java/org/gephi/graph/impl/UndirectedDecoratorTest.java similarity index 100% rename from store/src/test/java/org/gephi/graph/impl/UndirectedDecoratorTest.java rename to src/test/java/org/gephi/graph/impl/UndirectedDecoratorTest.java diff --git a/store-benchmark/pom.xml b/store-benchmark/pom.xml deleted file mode 100644 index 967761f7..00000000 --- a/store-benchmark/pom.xml +++ /dev/null @@ -1,79 +0,0 @@ - - 4.0.0 - - org.gephi - graphstore-benchmark - 0.6.1-SNAPSHOT - jar - - graphstore-benchmark - http://maven.apache.org - - - UTF-8 - 0.6.0-SNAPSHOT - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.1 - - - org.apache.maven.plugins - maven-surefire-plugin - 2.17 - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-surefire-plugin - - -Xmx2g - - - - - - - - org.testng - testng - 6.8.5 - test - - - ${project.groupId} - graphstore - ${graphstore.version} - - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - true - - - false - - - - diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/DataStructureBenchmark.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/DataStructureBenchmark.java deleted file mode 100644 index cbb8904f..00000000 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/DataStructureBenchmark.java +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import cern.colt.bitvector.BitVector; -import cern.colt.map.OpenIntObjectHashMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap; -import it.unimi.dsi.fastutil.ints.IntAVLTreeSet; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntIterator; -import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectBigArrayBigList; -import it.unimi.dsi.fastutil.objects.ObjectBigList; -import it.unimi.dsi.fastutil.objects.ObjectList; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import org.gephi.graph.api.types.TimestampSet; - -import java.util.ArrayList; -import java.util.BitSet; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Random; - -/** - * - * @author mbastian - */ -public class DataStructureBenchmark { - - private static int NODES = 500000; - private static int LOW_NODES = 50000; - private Object object; - - /** - * Insertion and memory usage for Int2ObjectOpenHashMap - */ - public Runnable openHashMapMemory() { - return () -> { - int nodes = NODES; - Int2ObjectMap map = new Int2ObjectOpenHashMap(nodes); - for (int i = 0; i < nodes; i++) { - map.put(i, new Object()); - } - object = map; - }; - } - - /** - * Insertion and memory usage for Int2ObjectOpenHashMap without original - * capcity - */ - public Runnable dynamicOpenHashMapMemory() { - return () -> { - int nodes = NODES; - Int2ObjectMap map = new Int2ObjectOpenHashMap(); - for (int i = 0; i < nodes; i++) { - map.put(i, new Object()); - } - object = map; - }; - } - - /** - * Insertion and memory usage for Int2ObjectRBTreeMap - */ - public Runnable rbHashMapMemory() { - return () -> { - int nodes = NODES; - Int2ObjectMap map = new Int2ObjectRBTreeMap(); - for (int i = 0; i < nodes; i++) { - map.put(i, new Object()); - } - object = map; - }; - } - - /** - * Insertion and memory usage for OpenIntObjectHashMap - */ - public Runnable coltOpenHashMapMemory() { - return () -> { - int nodes = NODES; - OpenIntObjectHashMap map = new OpenIntObjectHashMap(nodes); - for (int i = 0; i < nodes; i++) { - map.put(i, new Object()); - } - object = map; - }; - } - - /** - * Insertion and memory usage for basic array - */ - public Runnable arrayMemory() { - return () -> { - int nodes = NODES; - Object[] array = new Object[nodes]; - for (int i = 0; i < nodes; i++) { - array[i] = new Object(); - } - object = array; - }; - } - - /** - * Insertion and memory usage for object list - */ - public Runnable objectListMemory() { - return () -> { - int nodes = NODES; - final ObjectList list = new ObjectArrayList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - object = list; - }; - } - - public Runnable dynamicObjectListMemory() { - return () -> { - final ObjectList list = new ObjectArrayList(); - int nodes = NODES; - for (int i = 0; i < nodes; i++) { - list.add(1); - } - object = list; - }; - } - - /** - * Insertion and memory usage for object list - */ - public Runnable objectBigListMemory() { - return () -> { - int nodes = NODES; - final ObjectBigList list = new ObjectBigArrayBigList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - object = list; - }; - } - - public Runnable openHashMapIteration() { - int nodes = NODES; - final Int2ObjectMap map = new Int2ObjectOpenHashMap(nodes); - for (int i = 0; i < nodes; i++) { - map.put(i, new Integer(1)); - } - return () -> { - int sum = 0; - for (Object i : map.values()) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable rbHashMapIteration() { - int nodes = NODES; - final Int2ObjectMap map = new Int2ObjectRBTreeMap(); - for (int i = 0; i < nodes; i++) { - map.put(i, new Integer(1)); - } - return () -> { - int sum = 0; - for (Object i : map.values()) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable arrayIteration() { - //Create array - int nodes = NODES; - final Object[] array = new Object[nodes]; - for (int i = 0; i < nodes; i++) { - array[i] = 1; - } - return () -> { - int sum = 0; - for (Object i : array) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable linkedListIteration() { - //Create array - int nodes = NODES; - final LinkedList list = new LinkedList(); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectListIteration() { - int nodes = NODES; - final ObjectList list = new ObjectArrayList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectBigListIteration() { - int nodes = NODES; - final ObjectBigList list = new ObjectBigArrayBigList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectLinkedHashMapIteration() { - int nodes = NODES; - final Int2ObjectLinkedOpenHashMap list = new Int2ObjectLinkedOpenHashMap(nodes); - for (int i = 0; i < nodes; i++) { - list.put(new Integer(i), new Integer(1)); - } - return () -> { - int sum = 0; - for (Object i : list.values()) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectHashObjectSet() { - int nodes = NODES; - final ObjectOpenHashSet list = new ObjectOpenHashSet(nodes); - for (int i = 0; i < nodes; i++) { - list.add(i); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectAVLObjectSet() { - int nodes = NODES; - final ObjectAVLTreeSet list = new ObjectAVLTreeSet(); - for (int i = 0; i < nodes; i++) { - list.add(i); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable openHashMapResetAll() { - int nodes = LOW_NODES; - final Int2IntOpenHashMap map = new Int2IntOpenHashMap(nodes); - for (int i = 0; i < nodes; i++) { - map.put(i, i); - } - return () -> { - int nodes1 = LOW_NODES; - for (int i = 0; i < nodes1; i++) { - map.put(i, i); - } - }; - } - - public Runnable arrayResetAll() { - int nodes = LOW_NODES; - final int[] array = new int[nodes]; - for (int i = 0; i < nodes; i++) { - array[i] = i; - } - return () -> { - int nodes1 = LOW_NODES; - for (int i = 0; i < nodes1; i++) { - array[i] = i; - } - }; - } - - public Runnable objectListResetAll() { - int nodes = LOW_NODES; - final IntList list = new IntArrayList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(i); - } - return () -> { - int nodes1 = LOW_NODES; - for (int i = 0; i < nodes1; i++) { - list.set(i, i); - } - }; - } - - public Runnable fastutilObject2IntIterate() { - int nodes = NODES; - final Object2IntMap map = new Object2IntOpenHashMap<>(); - for (int i = 0; i < nodes; i++) { - map.put("" + i, i); - } - final Random rand = new Random(456); - return () -> { - int matches = 0; - for (int i = 0; i < 50000; i++) { - int id = rand.nextInt(NODES - 1); - if (map.containsKey("" + id)) { - matches += id; - } - } - object = matches; - }; - } - - public Runnable javaObject2IntIterate() { - int nodes = NODES; - final Map map = new HashMap<>(); - for (int i = 0; i < nodes; i++) { - map.put("" + i, i); - } - final Random rand = new Random(456); - return () -> { - int matches = 0; - for (int i = 0; i < 50000; i++) { - int id = rand.nextInt(NODES - 1); - if (map.containsKey("" + id)) { - matches += id; - } - } - object = matches; - }; - } - - public Runnable javaArrayIterate() { - final boolean[] bitset = new boolean[NODES]; - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - bitset[i] = rand.nextBoolean(); - } - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset[i]; - if (b) { - cardinality++; - } - } - object = cardinality; - }; - } - - public Runnable javaBitVectorIterate() { - final BitSet bitset = new BitSet(NODES); - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - bitset.set(i, rand.nextBoolean()); - } - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset.get(i); - if (b) { - cardinality++; - } - } - object = cardinality; - }; - } - - public Runnable coltBitVector() { - final BitVector bitset = new BitVector(NODES); - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - if (rand.nextBoolean()) { - bitset.set(i); - } - } - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset.get(i); - if (b) { - cardinality++; - } - } - object = cardinality; - }; - } - - public Runnable coltBitVectorIterateAndMapLookup() { - final BitVector bitset = new BitVector(NODES); - final Int2IntOpenHashMap map = new Int2IntOpenHashMap(); - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - if (rand.nextBoolean()) { - bitset.set(i); - map.put(i, i); - } - } - map.trim(); - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset.get(i); - if (b) { - long rank = map.get(i); - cardinality += rank; - } - } - object = cardinality; - }; - } - - public Runnable javaBitVectorMemory() { - - return () -> { - BitSet bitset = new BitSet(10000000); - Random rand = new Random(23); - for (int i = 0; i < 10000000; i++) { - bitset.set(i, rand.nextBoolean()); - } - object = bitset; - }; - } - - public Runnable coltBitVectorMemory() { - - return () -> { - BitVector bitset = new BitVector(10000000); - Random rand = new Random(23); - for (int i = 0; i < 10000000; i++) { - if (rand.nextBoolean()) { - bitset.set(i); - } - } - object = bitset; - }; - } - - public Runnable sparseArrayIterate(float emptyRatio) { - final BitVector bitset = new BitVector(NODES); - final int[] values = new int[NODES]; - Random rand2 = new Random(456); - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - if (rand.nextDouble() < emptyRatio) { - bitset.set(i); - } - values[i] = rand2.nextInt(NODES); - } - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset.get(i); - if (b) { - int val = values[i]; - cardinality += val; - } - } - object = cardinality; - }; - } - - public Runnable intHashSetIterate(float emptyRatio) { - int nodes = (int) (NODES * (1 - emptyRatio)); - Random rand2 = new Random(456); - final IntSet values = new IntOpenHashSet(nodes); - for (int i = 0; i < nodes; i++) { - values.add(rand2.nextInt(NODES)); - } - return () -> { - int cardinality = 0; - IntIterator itr = values.iterator(); - while (itr.hasNext()) { - int i = itr.nextInt(); - cardinality += i; - } - object = cardinality; - }; - } - - public Runnable intAVLSetIterate(float emptyRatio) { - int nodes = (int) (NODES * (1 - emptyRatio)); - Random rand2 = new Random(456); - final IntSet values = new IntAVLTreeSet(); - for (int i = 0; i < nodes; i++) { - values.add(rand2.nextInt(NODES)); - } - return () -> { - int cardinality = 0; - IntIterator itr = values.iterator(); - while (itr.hasNext()) { - int i = itr.nextInt(); - cardinality += i; - } - object = cardinality; - }; - } - - public Runnable arraySetMemory() { - final int size = 10000; - final int timestamps = 100; - - return () -> { - List trees = new ArrayList<>(); - Random rand = new Random(1234); - for (int i = 0; i < size; i++) { - TimestampSet set = new TimestampSet(timestamps); - for (int j = 0; j < timestamps; j++) { - final double val = rand.nextInt(timestamps); - set.add(val); - trees.add(set); - } - } - object = trees; - }; - } -} diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/EdgeStoreBenchmark.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/EdgeStoreBenchmark.java deleted file mode 100644 index e36f450b..00000000 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/EdgeStoreBenchmark.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.gephi.graph.benchmark; - -import java.util.Iterator; -import java.util.List; - -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.Node; -import org.gephi.graph.impl.EdgeStore; - -/** - * - * @author mbastian, niteshbhargv - */ -public class EdgeStoreBenchmark { - - private Object object; - - public Runnable pushEdgeStore(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - final List nodeList = graph.getNodes(); - final List edgeList = graph.getEdges(); - graph.getStore().addAllNodes(nodeList); - - Runnable runnable = () -> { - edgeStore.clear(); - for(Edge edge : edgeList) { - edgeStore.add(edge); - } - }; - return runnable; - } - - public Runnable iterateEdgeStore(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - - Runnable runnable = () -> { - Iterator itr = edgeStore.iterator(); - for (; itr.hasNext();) { - object = itr.next(); - } - }; - return runnable; - } - - public Runnable iterateEdgeStoreNeighborsOut(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - final List nodeList = graph.getNodes(); - - Runnable runnable = () -> { - for (Node node : nodeList) { - Iterator itr = edgeStore.edgeOutIterator(node); - for (; itr.hasNext();) { - object = itr.next(); - } - } - }; - return runnable; - } - - public Runnable iterateEdgeStoreNeighborsInOut(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - final List nodeList = graph.getNodes(); - - Runnable runnable = () -> { - for (Node node : nodeList) { - Iterator itr = edgeStore.edgeIterator(node); - for (; itr.hasNext();) { - object = itr.next(); - } - } - }; - return runnable; - } - - public Runnable resetEdgeStore(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - final List edgeList = graph.getEdges(); - - Runnable runnable = () -> { - for (Edge e : edgeList) { - edgeStore.remove(e); - } - for (Edge e : edgeList) { - edgeStore.add(e); - } - }; - return runnable; - } -} diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/Generator.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/Generator.java deleted file mode 100644 index ec8ee1da..00000000 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/Generator.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.gephi.graph.benchmark; - -import java.util.ArrayList; -import java.util.List; - -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.GraphFactory; -import org.gephi.graph.api.Node; -import org.gephi.graph.impl.GraphModelImpl; -import org.gephi.graph.impl.GraphStore; - -public abstract class Generator { - - protected final GraphFactory factory; - protected final GraphStore graphStore; - protected List nodes; - protected List edges; - - public Generator() { - this(new Configuration()); - } - - public Generator(final Configuration config) { - GraphModelImpl model = new GraphModelImpl(config); - factory = model.factory(); - graphStore = model.getStore(); - nodes = new ArrayList<>(); - edges = new ArrayList<>(); - } - - public GraphStore getStore() { - return graphStore; - } - - public List getNodes() { - return nodes; - } - - public List getEdges() { - return edges; - } - - public void clean() { - nodes = null; - edges = null; - } - - public abstract Generator generate(); - - public abstract Generator commit(); - - protected void commitInner() { - for (Node node : nodes) { - graphStore.addNode(node); - } - for (Edge edge : edges) { - graphStore.addEdge(edge); - } - } -} diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/KleinbergGraph.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/KleinbergGraph.java deleted file mode 100644 index 46019a24..00000000 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/KleinbergGraph.java +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.Node; - -import java.util.Random; - -/** - * Generates a directed connected graph. - * - * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.117.7097&rep=rep1&type=pdf - * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.83.381&rep=rep1&type=pdf - * - * n >= 2 p >= 1 p <= 2n - 2 q >= 0 q <= n^2 - p * (p + 3) / 2 - 1 for p < n q - * <= (2n - p - 3) * (2n - p) / 2 + 1 for p >= n r >= 0 - * - * Ω(n^4 * q) - * - * @author Nitesh Bhargava - */ -public class KleinbergGraph extends Generator { - - private int n = 5; - private int p = 2; - private int q = 2; - private int r = 0; - private boolean torusBased; - - /** - * User defined Kleinberg Graph no*no = number of nodes local = local - * contacts Long = long range contacts - */ - KleinbergGraph(int no, int local, int longRange) { - super(); - n = no; - p = local; - q = longRange; - r = 0; - torusBased = false; - } - - @Override - public KleinbergGraph generate() { - Random random = new Random(); - - // Creating lattice n x n - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - Node node = factory.newNode(); - nodes.add(node); - } - } - LongOpenHashSet edgeSet = new LongOpenHashSet(); - - // Creating edges from each node to p local contacts - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - for (int k = i - p; k <= i + p; ++k) { - for (int l = j - p; l <= j + p; ++l) { - if ((isTorusBased() || !isTorusBased() && k >= 0 && k < n && l >= 0 && l < n) - && d(i, j, k, l) <= p && nodes.get(i * n + j) != nodes.get(((k + n) % n) * n + ((l + n) % n))) { - Edge edge = factory.newEdge(nodes.get(i * n + j), nodes.get(((k + n) % n) * n + ((l + n) % n)), 0, true); - Object id = edge.getId(); - if (id instanceof Number) { - edges.add(edge); - edgeSet.add(((Number) id).longValue()); - } - } - } - } - } - } - - // Creating edges from each node to q long-range contacts - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - double sum = 0.0; - for (int k = 0; k < n; ++k) { - for (int l = 0; l < n; ++l) { - if (!isTorusBased() && d(i, j, k, l) > p) { - sum += Math.pow(d(i, j, k, l), -r); - } else if (isTorusBased() && dtb(i, j, k, l) > p) { - sum += Math.pow(dtb(i, j, k, l), -r); - } - - } - } - for (int m = 0; m < q; ++m) { - double b = random.nextDouble(); - boolean e = false; - while (!e) { - double pki = 0.0; - for (int k = 0; k < n && !e; ++k) { - for (int l = 0; l < n && !e; ++l) { - if (!isTorusBased() && d(i, j, k, l) > p || isTorusBased() && dtb(i, j, k, l) > p) { - pki += Math.pow(!isTorusBased() ? d(i, j, k, l) : dtb(i, j, k, l), -r) / sum; - Edge edge = factory.newEdge(nodes.get(i * n + j), nodes.get(k * n + l), 0, true); - Object id = edge.getId(); - if (id instanceof Number) { - if (b <= pki && !edgeSet.contains(((Number) id).longValue())) { - edges.add(edge); - edgeSet.add(((Number) id).longValue()); - e = true; - } - } - } - } - } - b = random.nextDouble(); - } - - } - } - } - return this; - } - - @Override - public KleinbergGraph commit() { - commitInner(); - return this; - } - - private int d(int i, int j, int k, int l) { - return Math.abs(k - i) + Math.abs(l - j); - } - - private int dtb(int i, int j, int k, int l) { - return Math.min(Math.abs(k - i), n - Math.abs(k - i)) + Math.min(Math.abs(l - j), n - Math.abs(l - j)); - } - - public int getn() { - return n; - } - - public int getp() { - return p; - } - - public int getq() { - return q; - } - - public int getr() { - return r; - } - - public boolean isTorusBased() { - return torusBased; - } - - public void setn(int n) { - this.n = n; - } - - public void setp(int p) { - this.p = p; - } - - public void setq(int q) { - this.q = q; - } - - public void setr(int r) { - this.r = r; - } - - public void setTorusBased(boolean torusBased) { - this.torusBased = torusBased; - } -} diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/LockingBenchmark.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/LockingBenchmark.java deleted file mode 100644 index 1e40ba25..00000000 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/LockingBenchmark.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import java.util.Random; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class LockingBenchmark { - - private final DataStruture struture = new DataStruture(); - private double number; - private final int READS = 500; - private final int WRITES = 100; - private final int READER_THREADS = 4; - private final int WRITER_THREADS = 4; - - public Runnable readWithoutLock() { - return () -> { - for (int i = 0; i < READS; i++) { - struture.read(); - } - }; - } - - public Runnable readWithLock() { - final ReadWriteLock lock = new ReentrantReadWriteLock(true); - return () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - } - - public Runnable fairReadOnly() { - final ReadWriteLock lock = new ReentrantReadWriteLock(true); - return () -> { - Runnable r = () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(r); - thread.start(); - threads[i] = thread; - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable unfairReadOnly() { - final ReadWriteLock lock = new ReentrantReadWriteLock(false); - return () -> { - Runnable r = () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(r); - thread.start(); - threads[i] = thread; - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable fairWriteOnly() { - final ReadWriteLock lock = new ReentrantReadWriteLock(true); - return () -> { - Runnable r = () -> { - for (int i = 0; i < WRITES; i++) { - lock.writeLock().lock(); - struture.write(); - lock.writeLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(r); - thread.start(); - threads[i] = thread; - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable unfairWriteOnly() { - final ReadWriteLock lock = new ReentrantReadWriteLock(false); - return () -> { - Runnable r = () -> { - for (int i = 0; i < WRITES; i++) { - lock.writeLock().lock(); - struture.write(); - lock.writeLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(r); - thread.start(); - threads[i] = thread; - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable fairReadWrites() { - final ReadWriteLock lock = new ReentrantReadWriteLock(true); - return () -> { - Runnable reader = () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - Runnable writer = () -> { - for (int i = 0; i < WRITES; i++) { - lock.writeLock().lock(); - struture.write(); - lock.writeLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS + WRITER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(reader); - threads[i] = thread; - } - for (int i = 0; i < WRITER_THREADS; i++) { - Thread thread = new Thread(writer); - threads[READER_THREADS + i] = thread; - } - for (Thread thread : threads) { - thread.start(); - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable unfairReadWrites() { - final ReadWriteLock lock = new ReentrantReadWriteLock(false); - return () -> { - Runnable reader = () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - Runnable writer = () -> { - for (int i = 0; i < WRITES; i++) { - lock.writeLock().lock(); - struture.write(); - lock.writeLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS + WRITER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(reader); - threads[i] = thread; - } - for (int i = 0; i < WRITER_THREADS; i++) { - Thread thread = new Thread(writer); - threads[READER_THREADS + i] = thread; - } - for (Thread thread : threads) { - thread.start(); - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - private class DataStruture { - - private final int[] values = new int[10000]; - private final int readLoops = 10; - private final int writeLoops = 5; - - public DataStruture() { - Random rand = new Random(454); - for (int i = 0; i < values.length; i++) { - values[i] = rand.nextInt(values.length); - } - } - - public void write() { - Random rand = new Random(45445); - for (int i = 0; i < writeLoops; i++) { - for (int j = 0; j < values.length; j++) { - values[j] = rand.nextInt(values.length); - } - } - } - - public void read() { - double avg = 0; - for (int i = 0; i < readLoops; i++) { - double sum = 0; - for (int j = 0; j < values.length; j++) { - sum += values[j]; - } - sum /= values.length; - avg += sum; - } - avg /= readLoops; - number = avg; - } - } -} diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/NodeStoreBenchmark.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/NodeStoreBenchmark.java deleted file mode 100644 index 9e8131d6..00000000 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/NodeStoreBenchmark.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import java.util.Iterator; -import java.util.List; - -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Node; -import org.gephi.graph.impl.NodeImpl; -import org.gephi.graph.impl.NodeStore; - -/** - * - * @author mbastian, niteshbhargv - */ -public class NodeStoreBenchmark { - - private Object object; - - public Runnable iterateStore(final int nodes) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, 0, config).generate().commit(); - final NodeStore nodeStore = graph.getStore().getNodeStore(); - Runnable runnable = () -> { - Iterator m = nodeStore.iterator(); - for (; m.hasNext();) { - NodeImpl b = (NodeImpl) m.next(); - object = b; - } - }; - return runnable; - } - - public Runnable resetNodeStore(final int nodes) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, 0, config).generate().commit(); - final NodeStore nodeStore = graph.getStore().getNodeStore(); - final List nodeList = graph.getNodes(); - Runnable runnable = () -> { - for (Node n : nodeList) { - nodeStore.remove(n); - } - for (Node n : nodeList) { - nodeStore.add(n); - } - }; - return runnable; - } - - public Runnable pushStore(int nodes) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, 0, config).generate(); - final NodeStore nodeStore = graph.getStore().getNodeStore(); - final List nodeList = graph.getNodes(); - - Runnable runnable = () -> { - nodeStore.clear(); - for (Node n : nodeList) { - nodeStore.add(n); - } - }; - return runnable; - } -} diff --git a/store-benchmark/src/main/java/org/gephi/graph/benchmark/RandomGraph.java b/store-benchmark/src/main/java/org/gephi/graph/benchmark/RandomGraph.java deleted file mode 100644 index 82c10447..00000000 --- a/store-benchmark/src/main/java/org/gephi/graph/benchmark/RandomGraph.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import java.util.Random; - -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.Node; -import org.gephi.graph.impl.NodeImpl; - -/** - * Generates directed connected random graph with wiring probability p - * - * @author Mathieu Bastian, Nitesh Bhargava - */ -public class RandomGraph extends Generator { - - protected final int numberOfNodes; - protected final int numberOfEdges; - protected final double wiringProbability; - - public RandomGraph(int n, double p) { - this(n, p, new Configuration()); - } - - public RandomGraph(int n, double p, Configuration config) { - super(config); - numberOfNodes = n; - numberOfEdges = (int)(n*(n-1)*p); - wiringProbability = p; - } - - public RandomGraph(int nodes, int edges) { - this(nodes, edges, new Configuration()); - } - - public RandomGraph(int nodes, int edges, Configuration confi) { - this(nodes, ((double)edges)/(nodes*(nodes-1)), confi); - } - - @Override - public RandomGraph generate() { - Random random = new Random(); - - for (int i = 0; i < numberOfNodes; i++) { - Node node = factory.newNode(i); - nodes.add(node); - } - - if (wiringProbability > 0) { - for (int i = 0; i < numberOfNodes - 1; i++) { - NodeImpl source = graphStore.getNode(i); - for (int j = i + 1; j < numberOfNodes; j++) { - NodeImpl target = graphStore.getNode(j); - - if (random.nextDouble() < wiringProbability && source != target) { - Edge edge = factory.newEdge(source, target, 0, true); - edges.add(edge); - } - } - } - } - return this; - } - - @Override - public RandomGraph commit() { - commitInner(); - return this; - } -} diff --git a/store-benchmark/src/main/java/org/gephi/nanobench/NanoBench.java b/store-benchmark/src/main/java/org/gephi/nanobench/NanoBench.java deleted file mode 100644 index 0c73ccc5..00000000 --- a/store-benchmark/src/main/java/org/gephi/nanobench/NanoBench.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.nanobench; - -import java.lang.management.ManagementFactory; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Lightweight CPU and memory benchmarking utility.

Inspired from nanobench - * (http://code.google.com/p/nanobench/) - * - * @author mbastian - */ -public class NanoBench { - - public static NanoBench create() { - return new NanoBench(); - } - private static final Logger logger = Logger.getLogger(NanoBench.class.getSimpleName()); - private int numberOfMeasurement = 50; - private int numberOfWarmUp = 0; - private List listeners; - - public NanoBench() { - listeners = new ArrayList<>(2); - listeners.add(new CPUMeasure(logger)); - listeners.add(new MemoryUsage(logger)); - } - - public NanoBench measurements(int numberOfMeasurement) { - this.numberOfMeasurement = numberOfMeasurement; - return this; - } - - public NanoBench warmUps(int numberOfWarmups) { - this.numberOfWarmUp = numberOfWarmups; - return this; - } - - public NanoBench cpuAndMemory() { - listeners = new ArrayList<>(2); - listeners.add(new CPUMeasure(logger)); - listeners.add(new MemoryUsage(logger)); - return this; - } - - public static Logger getLogger() { - return logger; - } - - public NanoBench cpuOnly() { - listeners = new ArrayList<>(1); - listeners.add(new CPUMeasure(logger)); - return this; - } - - public NanoBench memoryOnly() { - listeners = new ArrayList<>(1); - listeners.add(new MemoryUsage(logger)); - return this; - } - - public void measure(String label, Runnable task) { - MemoryUtil.restoreJvm(); - doWarmup(task); - MemoryUtil.restoreJvm(); - stress(); - doMeasure(label, task); - stress(); - MemoryUtil.restoreJvm(); - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - static int[] arrayStress = new int[10000]; - - private void stress() { - int m = 0; - for (int j = 0; j < 100; j++) { - int dummy = 0; - for (int i = 1; i < arrayStress.length; i++) { - arrayStress[i] = (int) Math.round(Math.log(i)); - dummy += arrayStress[i - 1]; - } - m += dummy; - } - } - - private void doMeasure(String label, Runnable task) { - for (int i = 0; i < this.numberOfMeasurement; i++) { - TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState(label, i, this.numberOfMeasurement), task, listeners); - tmp.run(); - } - } - - private void doWarmup(Runnable task) { - for (int i = 0; i < this.numberOfWarmUp; i++) { - TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState("_warmup_", i, this.numberOfWarmUp), task, listeners); - tmp.run(); - } - } - - /** - * Decorated runnable which enables measurements. - */ - private static class TimeMeasureProxy implements Runnable { - - private MeasureState state; - private Runnable runnable; - private List listeners; - - public TimeMeasureProxy(MeasureState state, Runnable runnable, List listeners) { - super(); - this.state = state; - this.runnable = runnable; - this.listeners = listeners; - } - - @Override - public void run() { - this.state.startNow(); - this.runnable.run(); - this.state.endNow(); - if (!state.getLabel().equals("_warmup_")) { - notifyMeasurement(state); - } - } - - private void notifyMeasurement(MeasureState times) { - for (MeasureListener listener : this.listeners) { - listener.onMeasure(times); - } - } - } - - /** - * Interface for measure listeners. Measure listeners are called when a - * measurement is finished. - */ - private interface MeasureListener { - - void onMeasure(MeasureState state); - } - - /** - * Basic class to measure time spent in each measurement - */ - private static class MeasureState implements Comparable { - - private String label; - private long startTime; - private long endTime; - private long index; - private int measurement; - - public MeasureState(String label, long index, int measurement) { - super(); - this.label = label; - this.measurement = measurement; - this.index = index; - } - - public long getIndex() { - return index; - } - - public String getLabel() { - return label; - } - - public long getStartTime() { - return startTime; - } - - public long getEndTime() { - return endTime; - } - - public long getMeasurements() { - return measurement; - } - - public long getMeasureTime() { - return endTime - startTime; - } - - public void startNow() { - this.startTime = System.nanoTime(); - } - - public void endNow() { - this.endTime = System.nanoTime(); - } - - @Override - public int compareTo(MeasureState another) { - if (this.startTime > another.startTime) { - return -1; - } else if (this.startTime < another.startTime) { - return 1; - } else { - return 0; - } - } - } - - /** - * CPU time listener to calculate the average time spent in a measurement. - *

The listener is called at the end of each measurement and collect the - * time spent from the - * MeasureState instance. At the last measurement it shows the - * average time spent, the total time and the number of measurement per - * seconds. - */ - private static class CPUMeasure implements MeasureListener { - - private static final double BY_SECONDS = 1000000000.0; - private final Logger log; - private static final DecimalFormat decimalFormat = new DecimalFormat("#,##0.0000"); - private static final DecimalFormat integerFormat = new DecimalFormat("#,##0.0"); - private int count = 0; - private long timeUsed = 0; - - public CPUMeasure(Logger logger) { - this.log = logger; - } - - @Override - public void onMeasure(MeasureState state) { - count++; - outputMeasureInfo(state); - } - - private void outputMeasureInfo(MeasureState state) { - timeUsed += state.getMeasureTime(); - - if (isEnd(state)) { - long total = timeUsed; - - StringBuilder sb = new StringBuilder("\n"); - sb.append(state.getLabel()).append("\t").append("avg: ").append( - decimalFormat.format(total / state.getMeasurements() / 1000000.0)) - .append(" ms\t").append("total: ").append( - integerFormat.format(total / 1000000000.0)).append(" s\t").append( - " tps: ").append( - integerFormat.format(state.getMeasurements() - / (total / BY_SECONDS))).append("\t") - .append("running: ").append(count) - .append(" times"); - count = 0; - timeUsed = 0; - if (!state.getLabel().equals("_warmup_")) { - log.info(sb.toString()); - } - } - } - - private boolean isEnd(MeasureState state) { - return count == state.getMeasurements(); - } - } - - /** - * Memory usage listener to calculate the average memory usage.

The - * listener is called after each measurement and perform a full GC and - * calculate free memory. At the last measurement it shows the average - * memory usage. - */ - private static class MemoryUsage implements MeasureListener { - - private final Logger log; - private static final DecimalFormat integerFormat = new DecimalFormat("#,##0.000"); - private int count = 0; - private long memoryUsed = 0; - - public MemoryUsage(Logger logger) { - this.log = logger; - } - - @Override - public void onMeasure(MeasureState state) { - count++; - outputMeasureInfo(state); - } - - private void outputMeasureInfo(MeasureState state) { - MemoryUtil.restoreJvm(); - memoryUsed += MemoryUtil.memoryUsed(); - - if (isEnd(state)) { - StringBuilder sb = new StringBuilder("\n"); - sb.append("memory-usage: ").append(state.getLabel()).append("\t") - .append(format((memoryUsed / count) / (1024.0 * 1024.0))).append( - " Mb\n"); - count = 0; - memoryUsed = 0; - - if (!state.getLabel().equals("_warmup_")) { - log.info(sb.toString()); - } - } - } - - private String format(double value) { - return integerFormat.format(value); - } - - private boolean isEnd(MeasureState state) { - return count == state.getMeasurements(); - } - } - - /** - * Utility memory class to perform GC and calculate memory usage - */ - public static class MemoryUtil { - - /** - * Call GC until no more memory can be freed - */ - public static void restoreJvm() { - int maxRestoreJvmLoops = 10; - long memUsedPrev = memoryUsed(); - for (int i = 0; i < maxRestoreJvmLoops; i++) { - System.runFinalization(); - System.gc(); - - long memUsedNow = memoryUsed(); - // break early if have no more finalization and get constant mem used - if ((ManagementFactory.getMemoryMXBean() - .getObjectPendingFinalizationCount() == 0) - && (memUsedNow >= memUsedPrev)) { - break; - } else { - memUsedPrev = memUsedNow; - } - } - } - - /** - * Return the memory used in bytes - * - * @return heap memory used in bytes - */ - public static long memoryUsed() { - Runtime rt = Runtime.getRuntime(); - return rt.totalMemory() - rt.freeMemory(); - } - } -} diff --git a/store-benchmark/src/test/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java b/store-benchmark/src/test/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java deleted file mode 100644 index 2cc93cc6..00000000 --- a/store-benchmark/src/test/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import java.util.logging.Level; -import java.util.logging.Logger; -import org.gephi.graph.benchmark.util.ReporterHandler; -import org.gephi.nanobench.NanoBench; -import org.testng.Reporter; -import org.testng.annotations.BeforeSuite; -import org.testng.annotations.Test; - -public class ControlBenchmarkTest { - - @BeforeSuite - public void setUp() { - Logger logger = NanoBench.getLogger(); - logger.setUseParentHandlers(false); - logger.setLevel(Level.INFO); - logger.addHandler(new ReporterHandler()); - Reporter.setEscapeHtml(false); - } - - @Test - public void testControl() { - Runnable runnable = new Runnable() { - final int[] array = new int[10000000]; - int m = 0; - - @Override - public void run() { - int dummy = 0; - for (int doNotIgnoreMe : array) { - dummy += doNotIgnoreMe; - } - m += dummy; - } - }; - NanoBench.create().measure("control", runnable); - } -} diff --git a/store-benchmark/src/test/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java b/store-benchmark/src/test/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java deleted file mode 100644 index 6306a0c2..00000000 --- a/store-benchmark/src/test/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import org.gephi.nanobench.NanoBench; -import org.testng.annotations.Test; - -public class EdgeStoreBenchmarkTest { - - @Test - public void testPushStore() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2).measure("push edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().pushEdgeStore(nodes, prob)); - } - } - } - - @Test - public void testIterateStore() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2).measure("iterate edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().iterateEdgeStore(nodes, prob)); - } - } - } - - @Test - public void testIterateOutNeighbors() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2).measure("iterate neighbors list out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().iterateEdgeStoreNeighborsOut(nodes, prob)); - } - } - } - - @Test - public void testIterateInOutNeighbors() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2).measure("iterate neighbors list in&out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().iterateEdgeStoreNeighborsInOut(nodes, prob)); - } - } - } - - @Test - public void testResetEdgeStore() { - int[] n = {100, 1000, 5000}; - double[] p = {0.01, 0.1, 0.3}; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2).measure("reset edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark().resetEdgeStore(nodes, prob)); - } - } - } -} diff --git a/store-benchmark/src/test/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java b/store-benchmark/src/test/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java deleted file mode 100644 index ef722555..00000000 --- a/store-benchmark/src/test/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import org.gephi.nanobench.NanoBench; -import org.testng.annotations.Test; - -public class NodeStoreBenchmarkTest { - - @Test - public void testPushStore() { - int[] n = {100, 1000, 10000, 100000}; - for (int nodes : n) { - NanoBench.create().measurements(10).measure("push node store " + nodes, new NodeStoreBenchmark().pushStore(nodes)); - } - } - - @Test - public void testIterateStore() { - int[] n = {100, 1000, 10000, 100000}; - for (int nodes : n) { - NanoBench.create().cpuOnly().measurements(10).measure("iterate node store " + nodes, new NodeStoreBenchmark().iterateStore(nodes)); - } - } - - @Test - public void testResetNodeStore() { - int[] n = {100, 1000, 10000, 100000}; - for (int nodes : n) { - NanoBench.create().measurements(10).measure("reset node store "+nodes, new NodeStoreBenchmark().resetNodeStore(nodes)); - } - } -} diff --git a/store/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java b/store/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java deleted file mode 100644 index 9ec03293..00000000 --- a/store/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark.util; - -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import org.testng.Reporter; - -/** - * Bridge between Java Logging API and TestNG's Reporter - * - * @author mbastian - */ -public class ReporterHandler extends Handler { - - @Override - public void publish(LogRecord record) { - String prefix = ""; - if (record.getLevel().equals(Level.INFO)) { - prefix = "[INFO] "; - } else if (record.getLevel().equals(Level.WARNING)) { - prefix = "[WARN] "; - } else if (record.getLevel().equals(Level.SEVERE)) { - prefix = "[SEVERE] "; - } - Reporter.log(prefix + record.getMessage() + "
", true); - } - - @Override - public void flush() { - } - - @Override - public void close() throws SecurityException { - } -} From de2d966987583deaa317a4876a9d5d650886adb4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 16:11:55 +0100 Subject: [PATCH 027/271] Update README and GitHub workflows --- .github/workflows/ci.yml | 3 --- .github/workflows/pr.yml | 3 --- README.md | 4 ++-- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57f4d60e..9e83e155 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,9 +7,6 @@ on: jobs: build: runs-on: ubuntu-latest - defaults: - run: - working-directory: ./store steps: - uses: actions/checkout@v2 - name: Set up Maven Central Repository diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 0471851f..e6987c44 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -6,9 +6,6 @@ on: jobs: build_and_test: runs-on: ubuntu-latest - defaults: - run: - working-directory: ./store steps: - uses: actions/checkout@v2 - name: Set up JDK 11 diff --git a/README.md b/README.md index fa5b290f..351412d8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![build](https://github.com/gephi/graphstore/actions/workflows/ci.yml/badge.svg)](https://github.com/gephi/graphstore/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/gephi/graphstore/badge.svg?branch=master&service=github)](https://coveralls.io/github/gephi/graphstore?branch=master) -GraphStore is an in-memory graph structure implementation written in Java. It is designed to be powerful, efficient and robust. It's powering the Gephi software and supports large graphs in intensive applications. +GraphStore is an in-memory graph structure implementation written in Java. It's designed to be powerful, efficient and robust. It's powering the Gephi software and supports large graphs in intensive applications. ## Features Highlight @@ -17,6 +17,7 @@ GraphStore is an in-memory graph structure implementation written in Java. It is * Supports dynamic graphs (graphs over time) * Built-in index on attribute values * Fast and compact binary serialization +* Spatial indexing based on a quadtree ## Download @@ -40,7 +41,6 @@ For a complete list of dependencies, consult the `pom.xml` file. GraphStore uses Maven for building. - > cd store > mvn clean install ### How to test From 7b619a6c11beabfd634bb4d85292a5330aa4172d Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 16:19:25 +0100 Subject: [PATCH 028/271] Minor fix on ReportHandler --- .../java/org/gephi/graph/benchmark/util/ReporterHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java b/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java index 9ec03293..d1a166c2 100644 --- a/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java +++ b/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java @@ -37,7 +37,7 @@ public void publish(LogRecord record) { } else if (record.getLevel().equals(Level.SEVERE)) { prefix = "[SEVERE] "; } - Reporter.log(prefix + record.getMessage() + "
", true); + Reporter.log(prefix + record.getMessage(), true); } @Override From 8055a1ee5f0bed4e11311a51f846f0f84ff13281 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 16:20:18 +0100 Subject: [PATCH 029/271] Update README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 351412d8..122d421c 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,10 @@ GraphStore uses Maven for building. > mvn jacoco:report +## How to run the benchmark code + + > mvn integration-test + ## Contribute The source code is available under the Apache 2.0 license. Contributions are welcome. From 5cbf0ea5eb4c7835b307268477bd845554b43dd7 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 16:51:52 +0100 Subject: [PATCH 030/271] Release version 0.6.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2382e3c0..eafe1330 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.0-SNAPSHOT + 0.6.0 jar GraphStore From ccd5041f98debf125d59baed7564d65cb6dc89d5 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 27 Nov 2021 16:53:31 +0100 Subject: [PATCH 031/271] Upgrade to version 0.6.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eafe1330..7bc24111 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.0 + 0.6.1-SNAPSHOT jar GraphStore From 852879f9aa2a20c0f2c544ee712eb15a2fe07001 Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Sat, 27 Nov 2021 17:25:01 +0100 Subject: [PATCH 032/271] Fix typo --- src/main/java/org/gephi/graph/impl/NodeImpl.java | 2 +- src/main/java/org/gephi/graph/impl/NodesQuadTree.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/NodeImpl.java b/src/main/java/org/gephi/graph/impl/NodeImpl.java index 0b2c2edb..32f3a8a9 100644 --- a/src/main/java/org/gephi/graph/impl/NodeImpl.java +++ b/src/main/java/org/gephi/graph/impl/NodeImpl.java @@ -168,7 +168,7 @@ protected SpatialNodeDataImpl getSpatialData() { return properties.getSpatialData(); } - protected void setSpatialDate(SpatialNodeDataImpl spatialData) { + protected void setSpatialData(SpatialNodeDataImpl spatialData) { properties.setSpatialData(spatialData); } diff --git a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index f3c58f82..25fee124 100644 --- a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -98,7 +98,7 @@ public boolean addNode(NodeImpl item) { SpatialNodeDataImpl spatialData = item.getSpatialData(); if (spatialData == null) { spatialData = new SpatialNodeDataImpl(minX, minY, maxX, maxY); - item.setSpatialDate(spatialData); + item.setSpatialData(spatialData); quadTreeRoot.insert(item); return true; } else { From 1b640b8f266f3986d0ca96438137ec9e78b52946 Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Sat, 27 Nov 2021 17:46:26 +0100 Subject: [PATCH 033/271] Implement missing method --- src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java index 664ffc4b..ac27742a 100644 --- a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java @@ -11,7 +11,7 @@ /** * Graph spatial indexing interface. - * + * * @author Eduardo Ramos */ public class SpatialIndexImpl implements SpatialIndex { @@ -42,7 +42,11 @@ public EdgeIterable getEdgesInArea(Rect2D rect) { @Override public void getEdgesInArea(Rect2D rect, Consumer callback) { - // TODO + final EdgeIterable iterable = getEdgesInArea(rect); + + for (Edge edge : iterable) { + callback.accept(edge); + } } protected void clearNodes() { From 94949c331f21993f8ead5d54ad870ddf43e9db46 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 2 Dec 2021 17:10:20 +0100 Subject: [PATCH 034/271] Remove quad tree methods with callback --- .../org/gephi/graph/api/SpatialIndex.java | 4 -- .../gephi/graph/impl/GraphViewDecorator.java | 24 --------- .../org/gephi/graph/impl/NodesQuadTree.java | 50 ------------------- .../gephi/graph/impl/SpatialIndexImpl.java | 14 ------ 4 files changed, 92 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java index bbd5e976..ee41ddc5 100644 --- a/src/main/java/org/gephi/graph/api/SpatialIndex.java +++ b/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -11,9 +11,5 @@ public interface SpatialIndex { NodeIterable getNodesInArea(Rect2D rect); - void getNodesInArea(Rect2D rect, Consumer callback); - EdgeIterable getEdgesInArea(Rect2D rect); - - void getEdgesInArea(Rect2D rect, Consumer callback); } diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index b17cd8a0..3efb8bb7 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -762,36 +762,12 @@ public NodeIterable getNodesInArea(Rect2D rect) { return new NodeIterableWrapper(new NodeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); } - @Override - public void getNodesInArea(Rect2D rect, final Consumer callback) { - graphStore.spatialIndex.getNodesInArea(rect, new Consumer() { - @Override - public void accept(Node node) { - if (view.containsNode((NodeImpl) node)) { - callback.accept(node); - } - } - }); - } - @Override public EdgeIterable getEdgesInArea(Rect2D rect) { Iterator iterator = graphStore.spatialIndex.getEdgesInArea(rect).iterator(); return new EdgeIterableWrapper(new EdgeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); } - @Override - public void getEdgesInArea(Rect2D rect, final Consumer callback) { - graphStore.spatialIndex.getEdgesInArea(rect, new Consumer() { - @Override - public void accept(Edge edge) { - if (view.containsEdge((EdgeImpl) edge)) { - callback.accept(edge); - } - } - }); - } - protected final class NodeViewIterator implements Iterator { private final Iterator nodeIterator; diff --git a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index 25fee124..e9fe1bc4 100644 --- a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -47,26 +47,14 @@ public NodeIterable getNodes(Rect2D searchRect) { return quadTreeRoot.getNodes(searchRect); } - public void getNodes(Rect2D searchRect, Consumer callback) { - quadTreeRoot.getNodes(searchRect, callback); - } - public NodeIterable getNodes(float minX, float minY, float maxX, float maxY) { return quadTreeRoot.getNodes(new Rect2D(minX, minY, maxX, maxY)); } - public void getNodes(float minX, float minY, float maxX, float maxY, Consumer callback) { - quadTreeRoot.getNodes(new Rect2D(minX, minY, maxX, maxY), callback); - } - public NodeIterable getAllNodes() { return quadTreeRoot.getAllNodes(); } - public void getAllNodes(Consumer callback) { - quadTreeRoot.getAllNodes(callback); - } - public boolean updateNode(NodeImpl item, float minX, float minY, float maxX, float maxY) { writeLock(); try { @@ -456,44 +444,6 @@ private NodeIterable getAllNodes() { return new QuadTreeNodesIterable(null); } - private void getNodes(Rect2D searchRect, Consumer callback) { - if (searchRect.contains(this.rect)) { - this.getAllNodes(callback); - } else if (searchRect.intersects(this.rect)) { - if (objects != null && !objects.isEmpty()) { - for (NodeImpl obj : objects) { - SpatialNodeDataImpl spatialData = obj.getSpatialData(); - if (searchRect - .intersects(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY)) { - callback.accept(obj); - } - } - } - - if (childTL != null) { - childTL.getNodes(searchRect, callback); - childTR.getNodes(searchRect, callback); - childBL.getNodes(searchRect, callback); - childBR.getNodes(searchRect, callback); - } - } - } - - private void getAllNodes(Consumer callback) { - if (objects != null && !objects.isEmpty()) { - for (NodeImpl obj : objects) { - callback.accept(obj); - } - } - - if (childTL != null) { - childTL.getAllNodes(callback); - childTR.getAllNodes(callback); - childBL.getAllNodes(callback); - childBR.getAllNodes(callback); - } - } - private void update(NodeImpl item) { SpatialNodeDataImpl spatialData = item.getSpatialData(); if (spatialData.quadTreeNode != null) { diff --git a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java index ac27742a..8ab48ef4 100644 --- a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java @@ -30,25 +30,11 @@ public NodeIterable getNodesInArea(Rect2D rect) { return nodesTree.getNodes(rect); } - @Override - public void getNodesInArea(Rect2D rect, Consumer callback) { - nodesTree.getNodes(rect, callback); - } - @Override public EdgeIterable getEdgesInArea(Rect2D rect) { return new EdgeIterableWrapper(new EdgeIterator(rect, nodesTree.getNodes(rect).iterator()), nodesTree.lock); } - @Override - public void getEdgesInArea(Rect2D rect, Consumer callback) { - final EdgeIterable iterable = getEdgesInArea(rect); - - for (Edge edge : iterable) { - callback.accept(edge); - } - } - protected void clearNodes() { nodesTree.clear(); } From 230cce5765f03f04aa86961aa9b24a6b10d39137 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 3 Dec 2021 08:50:23 +0100 Subject: [PATCH 035/271] Add tests for infinity parsing/printing in AttributeUtils --- .../org/gephi/graph/impl/AttributeUtilsTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java index 11ce8d7f..0f3dcd70 100644 --- a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java +++ b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java @@ -300,6 +300,13 @@ public void testParseEmpty() { Assert.assertNull(AttributeUtils.parse("", Integer.class)); } + @Test + public void testParseInfinity() { + Assert.assertEquals(AttributeUtils.parse("Infinity", Double.class), Double.POSITIVE_INFINITY); + Assert.assertEquals(AttributeUtils.parse("+Infinity", Double.class), Double.POSITIVE_INFINITY); + Assert.assertEquals(AttributeUtils.parse("-Infinity", Double.class), Double.NEGATIVE_INFINITY); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testParseUnsupportedType() { AttributeUtils.parse("test", Color.class); @@ -691,6 +698,12 @@ public void testPrint() { .toString(TimeFormat.DATETIME, DateTimeZone.forID("+00:30"))); } + @Test + public void testPrintInfinity() { + Assert.assertEquals(AttributeUtils.print(Double.POSITIVE_INFINITY), "Infinity"); + Assert.assertEquals(AttributeUtils.print(Double.NEGATIVE_INFINITY), "-Infinity"); + } + @Test public void testIsNumberType() { Assert.assertTrue(AttributeUtils.isNumberType(Integer.class)); From 336562962f6b24acf01c1e0604ccfb23589e58a9 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 25 Dec 2021 15:40:34 +0100 Subject: [PATCH 036/271] Expose lock into the API for integrity testing #145 --- src/main/java/org/gephi/graph/api/Graph.java | 8 +++ .../java/org/gephi/graph/api/GraphLock.java | 70 +++++++++++++++++++ src/main/java/org/gephi/graph/api/Table.java | 7 ++ .../java/org/gephi/graph/api/TableLock.java | 31 ++++++++ .../org/gephi/graph/impl/ColumnStore.java | 4 +- .../gephi/graph/impl/EdgeIterableWrapper.java | 2 +- .../java/org/gephi/graph/impl/EdgeStore.java | 4 +- .../graph/impl/ElementIterableWrapper.java | 4 +- .../{GraphLock.java => GraphLockImpl.java} | 20 +++++- .../java/org/gephi/graph/impl/GraphStore.java | 9 ++- .../gephi/graph/impl/GraphViewDecorator.java | 6 ++ .../java/org/gephi/graph/impl/IndexImpl.java | 2 +- .../java/org/gephi/graph/impl/IndexStore.java | 2 +- .../gephi/graph/impl/IntervalIndexStore.java | 2 +- .../gephi/graph/impl/NodeIterableWrapper.java | 2 +- .../java/org/gephi/graph/impl/NodeStore.java | 4 +- .../org/gephi/graph/impl/NodesQuadTree.java | 3 +- .../java/org/gephi/graph/impl/TableImpl.java | 6 ++ .../{TableLock.java => TableLockImpl.java} | 12 +++- .../org/gephi/graph/impl/TimeIndexImpl.java | 2 +- .../org/gephi/graph/impl/TimeIndexStore.java | 4 +- .../java/org/gephi/graph/impl/TimeStore.java | 4 +- .../gephi/graph/impl/TimestampIndexStore.java | 2 +- .../gephi/graph/impl/UndirectedDecorator.java | 6 ++ .../org/gephi/graph/impl/BasicGraphStore.java | 6 ++ ...phLockTest.java => GraphLockImplTest.java} | 26 +++++-- 26 files changed, 215 insertions(+), 33 deletions(-) create mode 100644 src/main/java/org/gephi/graph/api/GraphLock.java create mode 100644 src/main/java/org/gephi/graph/api/TableLock.java rename src/main/java/org/gephi/graph/impl/{GraphLock.java => GraphLockImpl.java} (84%) rename src/main/java/org/gephi/graph/impl/{TableLock.java => TableLockImpl.java} (78%) rename src/test/java/org/gephi/graph/impl/{GraphLockTest.java => GraphLockImplTest.java} (68%) diff --git a/src/main/java/org/gephi/graph/api/Graph.java b/src/main/java/org/gephi/graph/api/Graph.java index 2ba7d1a7..a24aa67c 100644 --- a/src/main/java/org/gephi/graph/api/Graph.java +++ b/src/main/java/org/gephi/graph/api/Graph.java @@ -497,4 +497,12 @@ public interface Graph { * Closes a write lock for the current thread. */ public void writeUnlock(); + + /** + * Returns the graph lock, in case locking is enabled. The graph lock controls + * the multi-thread access to the graph structure. + * + * @return graph lock + */ + GraphLock getLock(); } diff --git a/src/main/java/org/gephi/graph/api/GraphLock.java b/src/main/java/org/gephi/graph/api/GraphLock.java new file mode 100644 index 00000000..457cf8d1 --- /dev/null +++ b/src/main/java/org/gephi/graph/api/GraphLock.java @@ -0,0 +1,70 @@ +package org.gephi.graph.api; + +/** + * Wrapper around ReentrantReadWriteLock that controls multi-thread + * access to the graph structure. + */ +public interface GraphLock { + + /** + * Acquires the read lock. Acquires the read lock if the write lock is not held + * by another thread and returns immediately. + */ + void readLock(); + + /** + * Attempts to release this lock. If the number of readers is now zero then the + * lock is made available for write lock attempts. If the current thread does + * not hold this lock then IllegalMonitorStateException is thrown. + * + * @throws IllegalMonitorStateException if the current thread does not hold this + * lock + */ + void readUnlock(); + + /** + * Release this lock by releasing all current read locks. + */ + void readUnlockAll(); + + /** + * Acquires the write lock. Acquires the write lock if neither the read nor + * write lock are held by another thread and returns immediately, setting the + * write lock hold count to one. + * + * @throws IllegalMonitorStateException if the current thread holds a read lock + * already + */ + void writeLock(); + + /** + * Attempts to release this lock. If the current thread is the holder of this + * lock then the hold count is decremented. If the hold count is now zero then + * the lock is released. If the current thread is not the holder of this lock + * then IllegalMonitorStateException is thrown. + *

+ * throws @IllegalMonitorStateException if the current thread does not hold this + * lock + */ + void writeUnlock(); + + /** + * Queries the number of reentrant read holds on this lock by the current + * thread. A reader thread has a hold on a lock for each lock action that is not + * matched by an unlock action. + * + * @return the number of holds on the read lock by the current thread, or zero + * if the read lock is not held by the current thread + */ + int getReadHoldCount(); + + /** + * Queries the number of reentrant write holds on this lock by the current + * thread. A writer thread has a hold on a lock for each lock action that is not + * matched by an unlock action. + * + * @return the number of holds on the write lock by the current thread, or zero + * if the write lock is not held by the current thread + */ + int getWriteHoldCount(); +} diff --git a/src/main/java/org/gephi/graph/api/Table.java b/src/main/java/org/gephi/graph/api/Table.java index 24767117..d29e8ac1 100644 --- a/src/main/java/org/gephi/graph/api/Table.java +++ b/src/main/java/org/gephi/graph/api/Table.java @@ -144,4 +144,11 @@ public interface Table extends ColumnIterable { * @return true if node table, false otherwise */ public boolean isEdgeTable(); + + /** + * Returns the table lock, which controls the multi-thread access to the table. + * + * @return table lock + */ + TableLock getLock(); } diff --git a/src/main/java/org/gephi/graph/api/TableLock.java b/src/main/java/org/gephi/graph/api/TableLock.java new file mode 100644 index 00000000..0f342a0d --- /dev/null +++ b/src/main/java/org/gephi/graph/api/TableLock.java @@ -0,0 +1,31 @@ +package org.gephi.graph.api; + +public interface TableLock { + + /** + * Acquires the lock. Acquires the lock if it is not held by another thread and + * returns immediately, setting the lock hold count to one. + */ + void lock(); + + /** + * Attempts to release this lock. If the current thread is the holder of this + * lock then the hold count is decremented. If the hold count is now zero then + * the lock is released. If the current thread is not the holder of this lock + * then IllegalMonitorStateException is thrown. + * + * @throws IllegalMonitorStateException if the current thread does not hold this + * lock + */ + void unlock(); + + /** + * Queries the number of holds on this lock by the current thread. A thread has + * a hold on a lock for each lock action that is not matched by an unlock + * action. + * + * @return the number of holds on this lock by the current thread, or zero if + * this lock is not held by the current thread + */ + int getHoldCount(); +} diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index 79d0506b..32faadee 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -54,7 +54,7 @@ public class ColumnStore implements ColumnIterable { // Version protected final List observers; // Locking (optional) - protected final TableLock lock; + protected final TableLockImpl lock; // Variables protected int length; @@ -68,7 +68,7 @@ public ColumnStore(GraphStore graphStore, Class elementType, boolean indexed) } this.graphStore = graphStore; this.configuration = graphStore != null ? graphStore.configuration : new Configuration(); - this.lock = GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? new TableLock() : null; + this.lock = GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? new TableLockImpl() : null; this.garbageQueue = new ShortRBTreeSet(); this.idMap = new Object2ShortOpenHashMap<>(MAX_SIZE); this.columns = new ColumnImpl[MAX_SIZE]; diff --git a/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java b/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java index f8e58753..4cf675dd 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java @@ -10,7 +10,7 @@ public EdgeIterableWrapper(Iterator iterator) { super(iterator); } - public EdgeIterableWrapper(Iterator iterator, GraphLock lock) { + public EdgeIterableWrapper(Iterator iterator, GraphLockImpl lock) { super(iterator, lock); } diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index a0fad2c6..2b1e9314 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -52,7 +52,7 @@ public class EdgeStore implements Collection, EdgeIterable { protected int mutualEdgesSize; protected int[] mutualEdgesTypeSize; // Locking (optional) - protected final GraphLock lock; + protected final GraphLockImpl lock; // Version protected final GraphVersion version; // Types counting (optional) @@ -72,7 +72,7 @@ public EdgeStore() { this.spatialIndex = null; } - public EdgeStore(final EdgeTypeStore edgeTypeStore, final SpatialIndexImpl spatialIndex, final GraphLock lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { + public EdgeStore(final EdgeTypeStore edgeTypeStore, final SpatialIndexImpl spatialIndex, final GraphLockImpl lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { initStore(); this.lock = lock; this.edgeTypeStore = edgeTypeStore; diff --git a/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java b/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java index bcfd31af..88d1f2c4 100644 --- a/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java @@ -10,13 +10,13 @@ public abstract class ElementIterableWrapper implements ElementIterable { protected final Iterator iterator; - protected final GraphLock lock; + protected final GraphLockImpl lock; public ElementIterableWrapper(Iterator iterator) { this(iterator, null); } - public ElementIterableWrapper(Iterator iterator, GraphLock lock) { + public ElementIterableWrapper(Iterator iterator, GraphLockImpl lock) { this.iterator = iterator; this.lock = lock; } diff --git a/src/main/java/org/gephi/graph/impl/GraphLock.java b/src/main/java/org/gephi/graph/impl/GraphLockImpl.java similarity index 84% rename from src/main/java/org/gephi/graph/impl/GraphLock.java rename to src/main/java/org/gephi/graph/impl/GraphLockImpl.java index dafee14d..c2bca26c 100644 --- a/src/main/java/org/gephi/graph/impl/GraphLock.java +++ b/src/main/java/org/gephi/graph/impl/GraphLockImpl.java @@ -18,27 +18,31 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import org.gephi.graph.api.GraphLock; -public class GraphLock { +public class GraphLockImpl implements GraphLock { protected final ReentrantReadWriteLock readWriteLock; protected final ReadLock readLock; protected final WriteLock writeLock; - public GraphLock() { + public GraphLockImpl() { readWriteLock = new ReentrantReadWriteLock(); readLock = readWriteLock.readLock(); writeLock = readWriteLock.writeLock(); } + @Override public void readLock() { readLock.lock(); } + @Override public void readUnlock() { readLock.unlock(); } + @Override public void readUnlockAll() { final int nReadLocks = readWriteLock.getReadHoldCount(); for (int n = 0; n < nReadLocks; n++) { @@ -46,6 +50,7 @@ public void readUnlockAll() { } } + @Override public void writeLock() { if (readWriteLock.getReadHoldCount() > 0 && !readWriteLock.isWriteLockedByCurrentThread()) { throw new IllegalMonitorStateException( @@ -54,10 +59,21 @@ public void writeLock() { writeLock.lock(); } + @Override public void writeUnlock() { writeLock.unlock(); } + @Override + public int getReadHoldCount() { + return readWriteLock.getReadHoldCount(); + } + + @Override + public int getWriteHoldCount() { + return readWriteLock.getWriteHoldCount(); + } + public void checkHoldWriteLock() { if (!readWriteLock.isWriteLockedByCurrentThread()) { throw new IllegalMonitorStateException( diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index fd219076..d5d3d7d7 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -56,7 +56,7 @@ public class GraphStore implements DirectedGraph, DirectedSubgraph { // Factory protected final GraphFactoryImpl factory; // Lock - protected final GraphLock lock; + protected final GraphLockImpl lock; // Version protected final GraphVersion version; protected final List observers; @@ -78,7 +78,7 @@ public GraphStore() { public GraphStore(GraphModelImpl model) { configuration = model != null ? model.configuration : new Configuration(); graphModel = model; - lock = new GraphLock(); + lock = new GraphLockImpl(); edgeTypeStore = new EdgeTypeStore(); mainGraphView = new MainGraphView(); @@ -663,6 +663,11 @@ public void writeUnlock() { lock.writeUnlock(); } + @Override + public GraphLockImpl getLock() { + return lock; + } + protected void autoReadLock() { if (GraphStoreConfiguration.ENABLE_AUTO_LOCKING) { readLock(); diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 3efb8bb7..1b67d387 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -23,6 +23,7 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Interval; @@ -630,6 +631,11 @@ public void writeLock() { graphStore.lock.writeLock(); } + @Override + public GraphLockImpl getLock() { + return graphStore.lock; + } + @Override public void writeUnlock() { graphStore.lock.writeUnlock(); diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index 06884763..df915f5b 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -47,7 +47,7 @@ public class IndexImpl implements Index { - protected final TableLock lock; + protected final TableLockImpl lock; protected final ColumnStore columnStore; protected AbstractIndex[] columns; protected int columnsCount; diff --git a/src/main/java/org/gephi/graph/impl/IndexStore.java b/src/main/java/org/gephi/graph/impl/IndexStore.java index 8bb0c28b..211b0a10 100644 --- a/src/main/java/org/gephi/graph/impl/IndexStore.java +++ b/src/main/java/org/gephi/graph/impl/IndexStore.java @@ -30,7 +30,7 @@ public class IndexStore { protected final ColumnStore columnStore; - protected final TableLock lock; + protected final TableLockImpl lock; protected final IndexImpl mainIndex; protected final Map> viewIndexes; diff --git a/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java b/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java index c4f103d4..86f0063a 100644 --- a/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java @@ -22,7 +22,7 @@ public class IntervalIndexStore extends TimeIndexStore> { - public IntervalIndexStore(Class type, GraphLock lock, boolean indexed) { + public IntervalIndexStore(Class type, GraphLockImpl lock, boolean indexed) { super(type, lock, indexed, new Interval2IntTreeMap()); mainIndex = indexed ? new IntervalIndexImpl(this, true) : null; } diff --git a/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java b/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java index 429c9ae3..8961d327 100644 --- a/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java @@ -10,7 +10,7 @@ public NodeIterableWrapper(Iterator iterator) { super(iterator); } - public NodeIterableWrapper(Iterator iterator, GraphLock lock) { + public NodeIterableWrapper(Iterator iterator, GraphLockImpl lock) { super(iterator, lock); } diff --git a/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java index 9b0f3de0..b6bc47e8 100644 --- a/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -33,7 +33,7 @@ public class NodeStore implements Collection, NodeIterable { protected final EdgeStore edgeStore; protected final SpatialIndexImpl spatialIndex; // Locking (optional) - protected final GraphLock lock; + protected final GraphLockImpl lock; // Version protected final GraphVersion version; // Data @@ -56,7 +56,7 @@ public NodeStore() { this.spatialIndex = null; } - public NodeStore(final EdgeStore edgeStore, final SpatialIndexImpl spatialIndex, final GraphLock lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { + public NodeStore(final EdgeStore edgeStore, final SpatialIndexImpl spatialIndex, final GraphLockImpl lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { initStore(); this.lock = lock; this.edgeStore = edgeStore; diff --git a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index e9fe1bc4..1a95ffea 100644 --- a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -8,7 +8,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; -import java.util.function.Consumer; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; import org.gephi.graph.api.Rect2D; @@ -22,7 +21,7 @@ */ public class NodesQuadTree { - protected final GraphLock lock = new GraphLock(); + protected final GraphLockImpl lock = new GraphLockImpl(); private final QuadTreeNode quadTreeRoot; private final int maxLevels; diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index 1d2cd2b1..3fce5b6a 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -23,6 +23,7 @@ import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Table; +import org.gephi.graph.api.TableLock; import org.gephi.graph.api.TableObserver; import org.gephi.graph.api.Element; import org.gephi.graph.api.Graph; @@ -157,6 +158,11 @@ public boolean isEdgeTable() { return Edge.class.equals(store.elementType); } + @Override + public TableLockImpl getLock() { + return store.lock; + } + public void destroyTableObserver(TableObserver observer) { checkableTableObserver(observer); diff --git a/src/main/java/org/gephi/graph/impl/TableLock.java b/src/main/java/org/gephi/graph/impl/TableLockImpl.java similarity index 78% rename from src/main/java/org/gephi/graph/impl/TableLock.java rename to src/main/java/org/gephi/graph/impl/TableLockImpl.java index a618d024..cbc83839 100644 --- a/src/main/java/org/gephi/graph/impl/TableLock.java +++ b/src/main/java/org/gephi/graph/impl/TableLockImpl.java @@ -16,20 +16,28 @@ package org.gephi.graph.impl; import java.util.concurrent.locks.ReentrantLock; +import org.gephi.graph.api.TableLock; -public class TableLock { +public class TableLockImpl implements TableLock { protected final ReentrantLock lock; - public TableLock() { + public TableLockImpl() { lock = new ReentrantLock(); } + @Override public void lock() { lock.lock(); } + @Override public void unlock() { lock.unlock(); } + + @Override + public int getHoldCount() { + return lock.getHoldCount(); + } } diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java index 4514e49a..cdc55a63 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java @@ -31,7 +31,7 @@ public abstract class TimeIndexImpl, M extends TimeMap> implements TimeIndex { // Data - protected final GraphLock lock; + protected final GraphLockImpl lock; protected final TimeIndexStore timestampIndexStore; protected final boolean mainIndex; protected TimeIndexEntry[] timestamps; diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java index ef5172b9..9de8e69e 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java @@ -35,7 +35,7 @@ public abstract class TimeIndexStore, M extends TimeMap> { // Lock - protected final GraphLock graphLock; + protected final GraphLockImpl graphLock; // Element protected final Class elementType; // Timestamp index managament @@ -47,7 +47,7 @@ public abstract class TimeIndexStore, protected TimeIndexImpl mainIndex; protected final Map viewIndexes; - protected TimeIndexStore(Class type, GraphLock lock, boolean indexed, Map sortedMap) { + protected TimeIndexStore(Class type, GraphLockImpl lock, boolean indexed, Map sortedMap) { elementType = type; graphLock = lock; diff --git a/src/main/java/org/gephi/graph/impl/TimeStore.java b/src/main/java/org/gephi/graph/impl/TimeStore.java index 4d91a934..22d8abe4 100644 --- a/src/main/java/org/gephi/graph/impl/TimeStore.java +++ b/src/main/java/org/gephi/graph/impl/TimeStore.java @@ -24,12 +24,12 @@ public class TimeStore { protected final GraphStore graphStore; // Lock (optional - protected final GraphLock lock; + protected final GraphLockImpl lock; // Store protected TimeIndexStore nodeIndexStore; protected TimeIndexStore edgeIndexStore; - public TimeStore(GraphStore store, GraphLock graphLock, boolean indexed) { + public TimeStore(GraphStore store, GraphLockImpl graphLock, boolean indexed) { lock = graphLock; graphStore = store; diff --git a/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java b/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java index 6116150e..8c5bddf1 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java @@ -22,7 +22,7 @@ public class TimestampIndexStore extends TimeIndexStore> { - public TimestampIndexStore(Class type, GraphLock lock, boolean indexed) { + public TimestampIndexStore(Class type, GraphLockImpl lock, boolean indexed) { super(type, lock, indexed, new Double2IntRBTreeMap()); mainIndex = indexed ? new TimestampIndexImpl(this, true) : null; } diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index e02735c3..e38b22fd 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -20,6 +20,7 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Interval; @@ -344,6 +345,11 @@ public void writeUnlock() { store.autoWriteUnlock(); } + @Override + public GraphLockImpl getLock() { + return store.getLock(); + } + @Override public GraphModel getModel() { return store.graphModel; diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index 3605d86a..4acdfdeb 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -40,6 +40,7 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Element; +import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Interval; @@ -368,6 +369,11 @@ public void writeLock() { public void writeUnlock() { } + @Override + public GraphLock getLock() { + return null; + } + @Override public EdgeIterable getSelfLoops() { throw new UnsupportedOperationException("Not supported yet."); diff --git a/src/test/java/org/gephi/graph/impl/GraphLockTest.java b/src/test/java/org/gephi/graph/impl/GraphLockImplTest.java similarity index 68% rename from src/test/java/org/gephi/graph/impl/GraphLockTest.java rename to src/test/java/org/gephi/graph/impl/GraphLockImplTest.java index 27e15400..bda4193d 100644 --- a/src/test/java/org/gephi/graph/impl/GraphLockTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphLockImplTest.java @@ -18,11 +18,11 @@ import org.testng.Assert; import org.testng.annotations.Test; -public class GraphLockTest { +public class GraphLockImplTest { @Test public void testReadUnlockAll() { - GraphLock lock = new GraphLock(); + GraphLockImpl lock = new GraphLockImpl(); lock.readLock(); lock.readLock(); Assert.assertEquals(lock.readWriteLock.getReadHoldCount(), 2); @@ -32,7 +32,7 @@ public void testReadUnlockAll() { @Test public void testWriteLockBeforeReadLock() { - GraphLock lock = new GraphLock(); + GraphLockImpl lock = new GraphLockImpl(); lock.writeLock(); lock.readLock(); lock.readLock(); @@ -40,21 +40,35 @@ public void testWriteLockBeforeReadLock() { @Test(expectedExceptions = IllegalMonitorStateException.class) public void testWriteLockAfterReadLock() { - GraphLock lock = new GraphLock(); + GraphLockImpl lock = new GraphLockImpl(); lock.readLock(); lock.writeLock(); } @Test public void testCheckHoldWriteLock() { - GraphLock lock = new GraphLock(); + GraphLockImpl lock = new GraphLockImpl(); lock.writeLock(); lock.checkHoldWriteLock(); } @Test(expectedExceptions = IllegalMonitorStateException.class) public void testCheckHoldWriteLockFail() { - GraphLock lock = new GraphLock(); + GraphLockImpl lock = new GraphLockImpl(); lock.checkHoldWriteLock(); } + + @Test + public void testHoldersCount() { + GraphLockImpl lock = new GraphLockImpl(); + lock.readLock(); + Assert.assertEquals(lock.getReadHoldCount(), 1); + lock.readUnlock(); + Assert.assertEquals(lock.getReadHoldCount(), 0); + lock.writeLock(); + Assert.assertEquals(lock.getWriteHoldCount(), 1); + lock.writeUnlock(); + Assert.assertEquals(lock.getWriteHoldCount(), 0); + + } } From 22fcd4db0ab1cfbc02bfeac758b1a95075fbb00a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 25 Dec 2021 17:07:07 +0100 Subject: [PATCH 037/271] Make Table a collection of columns #143 --- .../org/gephi/graph/impl/ColumnStore.java | 2 +- .../java/org/gephi/graph/impl/TableImpl.java | 81 ++++++++++++++++++- .../org/gephi/graph/impl/TableImplTest.java | 34 ++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index 32faadee..8c4ad072 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -127,7 +127,7 @@ public void addColumn(final Column column) { } updateConfiguration(column); } else { - throw new IllegalArgumentException("The column already exist"); + throw new IllegalArgumentException("The column " + column.getId() + " already exist"); } } finally { unlock(); diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index 3fce5b6a..679ad923 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -15,6 +15,7 @@ */ package org.gephi.graph.impl; +import java.util.Collection; import java.util.Iterator; import java.util.List; import org.gephi.graph.api.AttributeUtils; @@ -28,7 +29,7 @@ import org.gephi.graph.api.Element; import org.gephi.graph.api.Graph; -public class TableImpl implements Table { +public class TableImpl implements Collection, Table { // Store protected final ColumnStore store; @@ -83,11 +84,28 @@ public Column addColumn(String id, String title, Class type, Origin origin, Obje return column; } + @Override + public boolean add(Column column) { + store.checkNonNullColumnObject(column); + store.addColumn(column); + return true; + } + @Override public int countColumns() { return store.size(); } + @Override + public int size() { + return countColumns(); + } + + @Override + public boolean isEmpty() { + return countColumns() == 0; + } + @Override public Iterator iterator() { return store.iterator(); @@ -103,6 +121,21 @@ public Column[] toArray() { return store.toArray(); } + @Override + public K[] toArray(K[] array) { + store.checkNonNullObject(array); + + ColumnImpl[] columns = store.toArray(); + + if (array.length < size()) { + array = (K[]) java.lang.reflect.Array.newInstance(array.getClass().getComponentType(), size()); + } + for (int i = 0; i < columns.length; i++) { + array[i] = (K) columns[i]; + } + return array; + } + @Override public List toList() { return store.toList(); @@ -123,6 +156,14 @@ public boolean hasColumn(String id) { return store.hasColumn(id.toLowerCase()); } + @Override + public boolean contains(Object o) { + store.checkNonNullColumnObject(o); + + ColumnImpl column = (ColumnImpl) o; + return hasColumn(column.getId()); + } + @Override public void removeColumn(Column column) { store.removeColumn(column); @@ -133,6 +174,38 @@ public void removeColumn(String id) { store.removeColumn(id.toLowerCase()); } + @Override + public boolean remove(Object o) { + store.checkNonNullColumnObject(o); + removeColumn((ColumnImpl) o); + return true; + } + + @Override + public void clear() { + store.clear(); + } + + @Override + public boolean containsAll(Collection c) { + throw new UnsupportedOperationException("This method from Collection isn't implemented"); + } + + @Override + public boolean addAll(Collection c) { + throw new UnsupportedOperationException("This method from Collection isn't implemented"); + } + + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException("This method from Collection isn't implemented"); + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException("This method from Collection isn't implemented"); + } + @Override public TableObserver createTableObserver(boolean withDiff) { return store.createTableObserver(this, withDiff); @@ -200,6 +273,12 @@ private void checkSupportedTypes(Class type) { } } + private void checkCollection(final Collection collection) { + if (collection == this) { + throw new IllegalArgumentException("Can't pass itself"); + } + } + private void checkDefaultValue(Object defaultValue, Class type) { if (defaultValue != null) { if (defaultValue.getClass() != type) { diff --git a/src/test/java/org/gephi/graph/impl/TableImplTest.java b/src/test/java/org/gephi/graph/impl/TableImplTest.java index 57e89eb1..26bd7a99 100644 --- a/src/test/java/org/gephi/graph/impl/TableImplTest.java +++ b/src/test/java/org/gephi/graph/impl/TableImplTest.java @@ -29,6 +29,8 @@ public class TableImplTest { public void testTable() { TableImpl table = new TableImpl<>(Node.class, false); Assert.assertEquals(table.countColumns(), 0); + Assert.assertEquals(table.size(), 0); + Assert.assertTrue(table.isEmpty()); } @Test @@ -146,6 +148,14 @@ public void testHasColumn() { Assert.assertTrue(table.hasColumn("iD")); } + @Test + public void testContains() { + TableImpl table = new TableImpl<>(Node.class, false); + Column col = table.addColumn("Id", Integer.class); + + Assert.assertTrue(table.contains(col)); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testGetColumnBadIndex() { TableImpl table = new TableImpl<>(Node.class, false); @@ -211,6 +221,15 @@ public void testRemoveColumn() { Assert.assertFalse(table.hasColumn("id")); } + @Test + public void testRemove() { + TableImpl table = new TableImpl<>(Node.class, false); + Column col = table.addColumn("Id", Integer.class); + + table.remove(col); + Assert.assertFalse(table.contains(col)); + } + @Test public void testRemoveColumnString() { TableImpl table = new TableImpl<>(Node.class, false); @@ -225,6 +244,14 @@ public void testRemoveColumnString() { Assert.assertFalse(table.hasColumn("Id")); } + @Test + public void testClear() { + TableImpl table = new TableImpl<>(Node.class, false); + table.addColumn("Id", Integer.class); + table.clear(); + Assert.assertTrue(table.isEmpty()); + } + @Test public void testCountColumns() { TableImpl table = new TableImpl<>(Node.class, false); @@ -248,6 +275,13 @@ public void testToArray() { Assert.assertEquals(table.toArray(), new Column[] { col }); } + @Test + public void testToArrayFromCollection() { + TableImpl table = new TableImpl<>(Node.class, false); + Column col = table.addColumn("Id", Integer.class); + Assert.assertEquals(table.toArray(new Column[0]), new Column[] { col }); + } + @Test public void testToList() { TableImpl table = new TableImpl<>(Node.class, false); From b3127d90697ef4ca4fe337969956d8a7d2adeb31 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Dec 2021 09:58:21 +0100 Subject: [PATCH 038/271] Refactoring separate AbstractIndex into a separate ColumnIndex class --- .../org/gephi/graph/impl/ColumnIndex.java | 616 ++++++++++++++++ .../java/org/gephi/graph/impl/IndexImpl.java | 693 ++---------------- .../org/gephi/graph/impl/IndexImplTest.java | 4 +- 3 files changed, 670 insertions(+), 643 deletions(-) create mode 100644 src/main/java/org/gephi/graph/impl/ColumnIndex.java diff --git a/src/main/java/org/gephi/graph/impl/ColumnIndex.java b/src/main/java/org/gephi/graph/impl/ColumnIndex.java new file mode 100644 index 00000000..bbe706d6 --- /dev/null +++ b/src/main/java/org/gephi/graph/impl/ColumnIndex.java @@ -0,0 +1,616 @@ +package org.gephi.graph.impl; + +import it.unimi.dsi.fastutil.booleans.BooleanArrays; +import it.unimi.dsi.fastutil.bytes.Byte2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.bytes.ByteArrays; +import it.unimi.dsi.fastutil.chars.Char2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.chars.CharArrays; +import it.unimi.dsi.fastutil.doubles.Double2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.doubles.DoubleArrays; +import it.unimi.dsi.fastutil.floats.Float2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.floats.FloatArrays; +import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.ints.IntArrays; +import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.longs.LongArrays; +import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrays; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.shorts.Short2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.shorts.ShortArrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import org.gephi.graph.api.Element; + +public abstract class ColumnIndex implements Iterable>> { + + // Const + public static final boolean TRIMMING_ENABLED = false; + public static final int TRIMMING_FREQUENCY = 30; + // Data + protected final ColumnImpl column; + protected final ValueSet nullSet; + protected Map> map; + // Variable + protected int elements; + + public ColumnIndex(ColumnImpl column) { + this.column = column; + this.nullSet = new ValueSet<>(null); + } + + public K putValue(T element, K value) { + if (value == null) { + if (nullSet.add(element)) { + elements++; + } + } else { + ValueSet set = getValueSet(value); + if (set == null) { + set = addValue(value); + } + value = set.value; + + if (set.add(element)) { + elements++; + } + } + return value; + } + + public void removeValue(T element, K value) { + if (value == null) { + if (nullSet.remove(element)) { + elements--; + } + } else { + ValueSet set = getValueSet(value); + if (set.remove(element)) { + elements--; + } + if (set.isEmpty()) { + removeValue(value); + } + } + } + + public K replaceValue(T element, K oldValue, K newValue) { + removeValue(element, oldValue); + return putValue(element, newValue); + } + + public int getCount(K value) { + if (value == null) { + return nullSet.size(); + } + ValueSet valueSet = getValueSet(value); + if (valueSet != null) { + return valueSet.size(); + } else { + return 0; + } + } + + public Collection values() { + return new WithNullDecorator(); + } + + public int countValues() { + return (nullSet.isEmpty() ? 0 : 1) + map.size(); + } + + public Number getMinValue() { + if (isSortable()) { + if (map.isEmpty()) { + return null; + } else { + return (Number) ((SortedMap) map).firstKey(); + } + } else { + throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column + .getTypeClass().getSimpleName() + ")."); + } + } + + public Number getMaxValue() { + if (isSortable()) { + if (map.isEmpty()) { + return null; + } else { + return (Number) ((SortedMap) map).lastKey(); + } + } else { + throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column + .getTypeClass().getSimpleName() + ")."); + } + } + + protected void destroy() { + map = null; + nullSet.clear(); + elements = 0; + } + + protected void clear() { + map.clear(); + nullSet.clear(); + elements = 0; + } + + @Override + public Iterator>> iterator() { + return new EntryIterator(); + } + + protected ValueSet getValueSet(K value) { + if (value == null) { + return nullSet; + } + return map.get(value); + } + + protected void removeValue(K value) { + map.remove(value); + } + + protected ValueSet addValue(K value) { + ValueSet valueSet = new ValueSet<>(value); + map.put(value, valueSet); + return valueSet; + } + + protected boolean isSortable() { + return Number.class.isAssignableFrom(column.getTypeClass()) && map instanceof SortedMap; + } + + protected static class DefaultIndex extends ColumnIndex { + + public DefaultIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenHashMap<>(); + } + } + + protected static class BooleanIndex extends ColumnIndex { + + public BooleanIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenHashMap<>(); + } + } + + protected static class DoubleIndex extends ColumnIndex { + + public DoubleIndex(ColumnImpl column) { + super(column); + + map = new Double2ObjectAVLTreeMap<>(); + } + } + + protected static class IntegerIndex extends ColumnIndex { + + public IntegerIndex(ColumnImpl column) { + super(column); + + map = new Int2ObjectAVLTreeMap<>(); + } + } + + protected static class FloatIndex extends ColumnIndex { + + public FloatIndex(ColumnImpl column) { + super(column); + + map = new Float2ObjectAVLTreeMap<>(); + } + } + + protected static class LongIndex extends ColumnIndex { + + public LongIndex(ColumnImpl column) { + super(column); + + map = new Long2ObjectAVLTreeMap<>(); + } + } + + protected static class ShortIndex extends ColumnIndex { + + public ShortIndex(ColumnImpl column) { + super(column); + + map = new Short2ObjectAVLTreeMap<>(); + } + } + + protected static class ByteIndex extends ColumnIndex { + + public ByteIndex(ColumnImpl column) { + super(column); + + map = new Byte2ObjectAVLTreeMap<>(); + } + } + + protected static class GenericNumberIndex extends ColumnIndex { + + public GenericNumberIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectAVLTreeMap<>(); + } + } + + protected static class CharIndex extends ColumnIndex { + + public CharIndex(ColumnImpl column) { + super(column); + + map = new Char2ObjectAVLTreeMap<>(); + } + } + + protected static class DefaultArrayIndex extends ColumnIndex { + + public DefaultArrayIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(ObjectArrays.HASH_STRATEGY); + } + } + + protected static class BooleanArrayIndex extends ColumnIndex { + + public BooleanArrayIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(BooleanArrays.HASH_STRATEGY); + } + } + + protected static class DoubleArrayIndex extends ColumnIndex { + + public DoubleArrayIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(DoubleArrays.HASH_STRATEGY); + } + } + + protected static class IntegerArrayIndex extends ColumnIndex { + + public IntegerArrayIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(IntArrays.HASH_STRATEGY); + } + } + + protected static class FloatArrayIndex extends ColumnIndex { + + public FloatArrayIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(FloatArrays.HASH_STRATEGY); + } + } + + protected static class LongArrayIndex extends ColumnIndex { + + public LongArrayIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(LongArrays.HASH_STRATEGY); + } + } + + protected static class ShortArrayIndex extends ColumnIndex { + + public ShortArrayIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(ShortArrays.HASH_STRATEGY); + } + } + + protected static class ByteArrayIndex extends ColumnIndex { + + public ByteArrayIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(ByteArrays.HASH_STRATEGY); + } + } + + protected static class CharArrayIndex extends ColumnIndex { + + public CharArrayIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(CharArrays.HASH_STRATEGY); + } + } + + protected static final class ValueSet implements Set { + + protected final K value; + private final Set set; + + public ValueSet(K value) { + this.value = value; + this.set = new ObjectOpenHashSet<>(); + } + + @Override + public int size() { + return set.size(); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return set.contains(o); + } + + @Override + public Iterator iterator() { + return set.iterator(); + } + + @Override + public Object[] toArray() { + return set.toArray(); + } + + @Override + public T[] toArray(T[] ts) { + return set.toArray(ts); + } + + @Override + public boolean add(T e) { + return set.add(e); + } + + @Override + public boolean remove(Object o) { + return set.remove(o); + } + + @Override + public boolean containsAll(Collection clctn) { + return set.containsAll(clctn); + } + + @Override + public boolean addAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported operation."); + } + + @Override + public boolean retainAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported operation."); + } + + @Override + public boolean removeAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported operation."); + } + + @Override + public void clear() { + set.clear(); + } + + @Override + public boolean equals(Object o) { + return set.equals(o); + } + + @Override + public int hashCode() { + return set.hashCode(); + } + } + + protected final class WithNullDecorator implements Collection { + + private boolean hasNull() { + return !nullSet.isEmpty(); + } + + @Override + public int size() { + return (hasNull() ? 1 : 0) + map.size(); + } + + @Override + public boolean isEmpty() { + return !hasNull() && map.isEmpty(); + } + + @Override + public boolean contains(Object o) { + if (o == null && hasNull()) { + return true; + } else if (o != null) { + return map.containsKey((K) o); + } + return false; + } + + @Override + public Iterator iterator() { + return new WithNullDecorator.WithNullIterator(); + } + + @Override + public Object[] toArray() { + if (hasNull()) { + Object[] res = new Object[map.size() + 1]; + res[0] = null; + System.arraycopy(map.keySet().toArray(), 0, res, 1, map.size()); + return res; + } else { + return map.keySet().toArray(); + } + } + + @Override + public V[] toArray(V[] array) { + + if (hasNull()) { + if (array.length < size()) { + array = (V[]) java.lang.reflect.Array + .newInstance(array.getClass().getComponentType(), map.size() + 1); + } + array[0] = null; + System.arraycopy(map.keySet().toArray(), 0, array, 1, map.size()); + return array; + } else { + return map.keySet().toArray(array); + } + } + + @Override + public boolean add(K e) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean containsAll(Collection clctn) { + for (Object o : clctn) { + if (o == null && nullSet.isEmpty()) { + return false; + } else if (o != null && !map.containsKey((K) o)) { + return false; + } + } + return true; + } + + @Override + public boolean addAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean removeAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean retainAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public void clear() { + throw new UnsupportedOperationException("Not supported"); + } + + private final class WithNullIterator implements Iterator { + + private final Iterator mapIterator; + private boolean hasNull; + + public WithNullIterator() { + hasNull = hasNull(); + mapIterator = map.keySet().iterator(); + } + + @Override + public boolean hasNext() { + if (hasNull) { + return true; + } + return mapIterator.hasNext(); + } + + @Override + public K next() { + if (hasNull) { + hasNull = false; + return null; + } + return mapIterator.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported operation."); + } + } + } + + private final class EntryIterator implements Iterator>> { + + private final Iterator>> mapIterator; + private NullEntry nullEntry; + + public EntryIterator() { + if (!nullSet.isEmpty()) { + nullEntry = new NullEntry(); + } + mapIterator = map.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + if (nullEntry != null) { + return true; + } + return mapIterator.hasNext(); + } + + @Override + public Map.Entry> next() { + if (nullEntry != null) { + NullEntry ne = nullEntry; + nullEntry = null; + return ne; + } + return mapIterator.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported operation."); + } + } + + private class NullEntry implements Map.Entry> { + + @Override + public K getKey() { + return null; + } + + @Override + public Set getValue() { + return nullSet; + } + + @Override + public Set setValue(Set v) { + throw new UnsupportedOperationException("Not supported operation."); + } + } +} diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index df915f5b..f6bc21d2 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -40,7 +40,6 @@ import java.util.Iterator; import java.util.Map; import java.util.Set; -import java.util.SortedMap; import org.gephi.graph.api.Column; import org.gephi.graph.api.Index; import org.gephi.graph.api.Element; @@ -49,12 +48,12 @@ public class IndexImpl implements Index { protected final TableLockImpl lock; protected final ColumnStore columnStore; - protected AbstractIndex[] columns; + protected ColumnIndex[] columns; protected int columnsCount; public IndexImpl(ColumnStore columnStore) { this.columnStore = columnStore; - this.columns = new AbstractIndex[0]; + this.columns = new ColumnIndex[0]; this.lock = columnStore.lock; } @@ -74,7 +73,7 @@ public int count(Column column, Object value) { lock(); try { - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index.getCount(value); } finally { unlock(); @@ -84,14 +83,14 @@ public int count(Column column, Object value) { public int count(String key, Object value) { checkNonNullObject(key); - AbstractIndex index = getIndex(key); + ColumnIndex index = getIndex(key); return index.getCount(value); } public Iterable get(String key, Object value) { checkNonNullObject(key); - AbstractIndex index = getIndex(key); + ColumnIndex index = getIndex(key); return index.getValueSet(value); } @@ -101,11 +100,11 @@ public Iterable get(Column column, Object value) { if (lock != null) { lock.lock(); - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); Set valueSet = index.getValueSet(value); return valueSet == null ? null : new LockableIterable<>(index.getValueSet(value)); } - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index.getValueSet(value); } @@ -115,7 +114,7 @@ public boolean isSortable(Column column) { lock(); try { - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index.isSortable(); } finally { @@ -129,7 +128,7 @@ public Number getMinValue(Column column) { lock(); try { - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index.getMinValue(); } finally { unlock(); @@ -141,7 +140,7 @@ public Number getMaxValue(Column column) { checkNonNullColumnObject(column); lock(); try { - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index.getMaxValue(); } finally { unlock(); @@ -151,7 +150,7 @@ public Number getMaxValue(Column column) { public Iterable>> get(Column column) { checkNonNullColumnObject(column); - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index; } @@ -161,7 +160,7 @@ public Collection values(Column column) { lock(); try { - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return new ArrayList(index.values()); } finally { unlock(); @@ -173,7 +172,7 @@ public int countValues(Column column) { checkNonNullColumnObject(column); lock(); try { - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index.countValues(); } finally { unlock(); @@ -185,7 +184,7 @@ public int countElements(Column column) { checkNonNullColumnObject(column); lock(); try { - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index.elements; } finally { unlock(); @@ -195,47 +194,47 @@ public int countElements(Column column) { public Object put(String key, Object value, T element) { checkNonNullObject(key); - AbstractIndex index = getIndex(key); + ColumnIndex index = getIndex(key); return index.putValue(element, value); } public Object put(Column column, Object value, T element) { checkNonNullColumnObject(column); - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index.putValue(element, value); } public void remove(String key, Object value, T element) { checkNonNullObject(key); - AbstractIndex index = getIndex(key); + ColumnIndex index = getIndex(key); index.removeValue(element, value); } public void remove(Column column, Object value, T element) { checkNonNullColumnObject(column); - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); index.removeValue(element, value); } public Object set(String key, Object oldValue, Object value, T element) { checkNonNullObject(key); - AbstractIndex index = getIndex(key); + ColumnIndex index = getIndex(key); return index.replaceValue(element, oldValue, value); } public Object set(Column column, Object oldValue, Object value, T element) { checkNonNullColumnObject(column); - AbstractIndex index = getIndex((ColumnImpl) column); + ColumnIndex index = getIndex((ColumnImpl) column); return index.replaceValue(element, oldValue, value); } public void clear() { - for (AbstractIndex ai : columns) { + for (ColumnIndex ai : columns) { if (ai != null) { ai.clear(); } @@ -245,7 +244,7 @@ public void clear() { protected void addColumn(ColumnImpl col) { if (col.isIndexed()) { ensureColumnSize(col.storeId); - AbstractIndex index = createIndex(col); + ColumnIndex index = createIndex(col); columns[col.storeId] = index; columnsCount++; } @@ -255,7 +254,7 @@ protected void addAllColumns(ColumnImpl[] cols) { ensureColumnSize(cols.length); for (ColumnImpl col : cols) { if (col.isIndexed()) { - AbstractIndex index = createIndex(col); + ColumnIndex index = createIndex(col); columns[col.storeId] = index; columnsCount++; } @@ -264,7 +263,7 @@ protected void addAllColumns(ColumnImpl[] cols) { protected void removeColumn(ColumnImpl col) { if (col.isIndexed()) { - AbstractIndex index = columns[col.storeId]; + ColumnIndex index = columns[col.storeId]; index.destroy(); columns[col.storeId] = null; columnsCount--; @@ -281,11 +280,11 @@ protected boolean hasColumn(ColumnImpl col) { return false; } - protected AbstractIndex getIndex(ColumnImpl col) { + protected ColumnIndex getIndex(ColumnImpl col) { if (col.isIndexed()) { int id = col.storeId; if (id != ColumnStore.NULL_ID && columns.length > id) { - AbstractIndex index = columns[id]; + ColumnIndex index = columns[id]; if (index != null && index.column == col) { return index; } @@ -294,7 +293,7 @@ protected AbstractIndex getIndex(ColumnImpl col) { return null; } - protected AbstractIndex getIndex(String key) { + protected ColumnIndex getIndex(String key) { int id = columnStore.getColumnIndex(key); if (id != ColumnStore.NULL_ID && columns.length > id) { return columns[id]; @@ -303,12 +302,12 @@ protected AbstractIndex getIndex(String key) { } protected void destroy() { - for (AbstractIndex ai : columns) { + for (ColumnIndex ai : columns) { if (ai != null) { ai.destroy(); } } - columns = new AbstractIndex[0]; + columns = new ColumnIndex[0]; columnsCount = 0; } @@ -316,74 +315,74 @@ protected int size() { return columnsCount; } - AbstractIndex createIndex(ColumnImpl column) { + ColumnIndex createIndex(ColumnImpl column) { if (column.getTypeClass().equals(Byte.class)) { // Byte - return new ByteIndex(column); + return new ColumnIndex.ByteIndex(column); } else if (column.getTypeClass().equals(Short.class)) { // Short - return new ShortIndex(column); + return new ColumnIndex.ShortIndex(column); } else if (column.getTypeClass().equals(Integer.class)) { // Integer - return new IntegerIndex(column); + return new ColumnIndex.IntegerIndex(column); } else if (column.getTypeClass().equals(Long.class)) { // Long - return new LongIndex(column); + return new ColumnIndex.LongIndex(column); } else if (column.getTypeClass().equals(Float.class)) { // Float - return new FloatIndex(column); + return new ColumnIndex.FloatIndex(column); } else if (column.getTypeClass().equals(Double.class)) { // Double - return new DoubleIndex(column); + return new ColumnIndex.DoubleIndex(column); } else if (Number.class.isAssignableFrom(column.getTypeClass())) { // Other numbers - return new GenericNumberIndex(column); + return new ColumnIndex.GenericNumberIndex(column); } else if (column.getTypeClass().equals(Boolean.class)) { // Boolean - return new BooleanIndex(column); + return new ColumnIndex.BooleanIndex(column); } else if (column.getTypeClass().equals(Character.class)) { // Char - return new CharIndex(column); + return new ColumnIndex.CharIndex(column); } else if (column.getTypeClass().equals(String.class)) { // String - return new DefaultIndex(column); + return new ColumnIndex.DefaultIndex(column); } else if (column.getTypeClass().equals(byte[].class)) { // Byte Array - return new ByteArrayIndex(column); + return new ColumnIndex.ByteArrayIndex(column); } else if (column.getTypeClass().equals(short[].class)) { // Short Array - return new ShortArrayIndex(column); + return new ColumnIndex.ShortArrayIndex(column); } else if (column.getTypeClass().equals(int[].class)) { // Integer Array - return new IntegerArrayIndex(column); + return new ColumnIndex.IntegerArrayIndex(column); } else if (column.getTypeClass().equals(long[].class)) { // Long Array - return new LongArrayIndex(column); + return new ColumnIndex.LongArrayIndex(column); } else if (column.getTypeClass().equals(float[].class)) { // Float array - return new FloatArrayIndex(column); + return new ColumnIndex.FloatArrayIndex(column); } else if (column.getTypeClass().equals(double[].class)) { // Double array - return new DoubleArrayIndex(column); + return new ColumnIndex.DoubleArrayIndex(column); } else if (column.getTypeClass().equals(boolean[].class)) { // Boolean array - return new BooleanArrayIndex(column); + return new ColumnIndex.BooleanArrayIndex(column); } else if (column.getTypeClass().equals(char[].class)) { // Char array - return new CharArrayIndex(column); + return new ColumnIndex.CharArrayIndex(column); } else if (column.getTypeClass().equals(String[].class)) { // String array - return new DefaultArrayIndex(column); + return new ColumnIndex.DefaultArrayIndex(column); } else if (column.getTypeClass().isArray()) { // Default Array - return new DefaultArrayIndex(column); + return new ColumnIndex.DefaultArrayIndex(column); } - return new DefaultIndex(column); + return new ColumnIndex.DefaultIndex(column); } private void ensureColumnSize(int index) { if (index >= columns.length) { - AbstractIndex[] newArray = new AbstractIndex[index + 1]; + ColumnIndex[] newArray = new ColumnIndex[index + 1]; System.arraycopy(columns, 0, newArray, 0, columns.length); columns = newArray; } @@ -416,594 +415,6 @@ void checkNonNullColumnObject(final Object o) { } } - protected abstract class AbstractIndex implements Iterable>> { - - // Const - public static final boolean TRIMMING_ENABLED = false; - public static final int TRIMMING_FREQUENCY = 30; - // Data - protected final ColumnImpl column; - protected final Set nullSet; - protected Map> map; - // Variable - protected int elements; - - public AbstractIndex(ColumnImpl column) { - this.column = column; - this.nullSet = new ObjectOpenHashSet<>(); - } - - public Object putValue(T element, Object value) { - if (value == null) { - if (nullSet.add(element)) { - elements++; - } - } else { - Set set = getValueSet((K) value); - if (set == null) { - set = addValue((K) value); - } - value = ((ValueSet) set).value; - - if (set.add(element)) { - elements++; - } - } - return value; - } - - public void removeValue(T element, Object value) { - if (value == null) { - if (nullSet.remove(element)) { - elements--; - } - } else { - Set set = getValueSet((K) value); - if (set.remove(element)) { - elements--; - } - if (set.isEmpty()) { - removeValue((K) value); - } - } - } - - public Object replaceValue(T element, K oldValue, K newValue) { - removeValue(element, oldValue); - return putValue(element, newValue); - } - - public int getCount(K value) { - if (value == null) { - return nullSet.size(); - } - Set valueSet = getValueSet(value); - if (valueSet != null) { - return valueSet.size(); - } else { - return 0; - } - } - - public Collection values() { - return new WithNullDecorator(); - } - - public int countValues() { - return (nullSet.isEmpty() ? 0 : 1) + map.size(); - } - - public Number getMinValue() { - if (isSortable()) { - if (map.isEmpty()) { - return null; - } else { - return (Number) ((SortedMap) map).firstKey(); - } - } else { - throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column - .getTypeClass().getSimpleName() + ")."); - } - } - - public Number getMaxValue() { - if (isSortable()) { - if (map.isEmpty()) { - return null; - } else { - return (Number) ((SortedMap) map).lastKey(); - } - } else { - throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column - .getTypeClass().getSimpleName() + ")."); - } - } - - protected void destroy() { - map = null; - nullSet.clear(); - elements = 0; - } - - protected void clear() { - map.clear(); - nullSet.clear(); - elements = 0; - } - - @Override - public Iterator>> iterator() { - return new EntryIterator(); - } - - protected Set getValueSet(K value) { - if (value == null) { - return nullSet; - } - return map.get(value); - } - - protected void removeValue(K value) { - map.remove(value); - } - - protected Set addValue(K value) { - ValueSet valueSet = new ValueSet(value); - map.put(value, valueSet); - return valueSet; - } - - protected boolean isSortable() { - return Number.class.isAssignableFrom(column.getTypeClass()) && map instanceof SortedMap; - } - - protected final class WithNullDecorator implements Collection { - - private boolean hasNull() { - return !nullSet.isEmpty(); - } - - @Override - public int size() { - return (hasNull() ? 1 : 0) + map.size(); - } - - @Override - public boolean isEmpty() { - return !hasNull() && map.isEmpty(); - } - - @Override - public boolean contains(Object o) { - if (o == null && hasNull()) { - return true; - } else if (o != null) { - return map.containsKey((K) o); - } - return false; - } - - @Override - public Iterator iterator() { - return new WithNullIterator(); - } - - @Override - public Object[] toArray() { - if (hasNull()) { - Object[] res = new Object[map.size() + 1]; - res[0] = null; - System.arraycopy(map.keySet().toArray(), 0, res, 1, map.size()); - return res; - } else { - return map.keySet().toArray(); - } - } - - @Override - public Object[] toArray(Object[] array) { - - if (hasNull()) { - if (array.length < size()) { - array = (K[]) java.lang.reflect.Array - .newInstance(array.getClass().getComponentType(), map.size() + 1); - } - array[0] = null; - System.arraycopy(map.keySet().toArray(), 0, array, 1, map.size()); - return array; - } else { - return map.keySet().toArray(array); - } - } - - @Override - public boolean add(Object e) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public boolean remove(Object o) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public boolean containsAll(Collection clctn) { - for (Object o : clctn) { - if (o == null && nullSet.isEmpty()) { - return false; - } else if (o != null && !map.containsKey((K) o)) { - return false; - } - } - return true; - } - - @Override - public boolean addAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public boolean removeAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public boolean retainAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public void clear() { - throw new UnsupportedOperationException("Not supported"); - } - - private final class WithNullIterator implements Iterator { - - private final Iterator mapIterator; - private boolean hasNull; - - public WithNullIterator() { - hasNull = hasNull(); - mapIterator = map.keySet().iterator(); - } - - @Override - public boolean hasNext() { - if (hasNull) { - return true; - } - return mapIterator.hasNext(); - } - - @Override - public K next() { - if (hasNull) { - hasNull = false; - return null; - } - return mapIterator.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Not supported operation."); - } - } - } - - private final class EntryIterator implements Iterator>> { - - private final Iterator>> mapIterator; - private NullEntry nullEntry; - - public EntryIterator() { - if (!nullSet.isEmpty()) { - nullEntry = new NullEntry(); - } - mapIterator = map.entrySet().iterator(); - } - - @Override - public boolean hasNext() { - if (nullEntry != null) { - return true; - } - return mapIterator.hasNext(); - } - - @Override - public Map.Entry> next() { - if (nullEntry != null) { - NullEntry ne = nullEntry; - nullEntry = null; - return ne; - } - return mapIterator.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Not supported operation."); - } - } - - private class NullEntry implements Map.Entry> { - - @Override - public K getKey() { - return null; - } - - @Override - public Set getValue() { - return nullSet; - } - - @Override - public Set setValue(Set v) { - throw new UnsupportedOperationException("Not supported operation."); - } - } - } - - private static final class ValueSet implements Set { - - private final K value; - private final Set set; - - public ValueSet(K value) { - this.value = value; - this.set = new ObjectOpenHashSet<>(); - } - - @Override - public int size() { - return set.size(); - } - - @Override - public boolean isEmpty() { - return set.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return set.contains(o); - } - - @Override - public Iterator iterator() { - return set.iterator(); - } - - @Override - public Object[] toArray() { - return set.toArray(); - } - - @Override - public T[] toArray(T[] ts) { - return set.toArray(ts); - } - - @Override - public boolean add(T e) { - return set.add(e); - } - - @Override - public boolean remove(Object o) { - return set.remove(o); - } - - @Override - public boolean containsAll(Collection clctn) { - return set.containsAll(clctn); - } - - @Override - public boolean addAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported operation."); - } - - @Override - public boolean retainAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported operation."); - } - - @Override - public boolean removeAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported operation."); - } - - @Override - public void clear() { - throw new UnsupportedOperationException("Not supported operation."); - } - - @Override - public boolean equals(Object o) { - return set.equals(o); - } - - @Override - public int hashCode() { - return set.hashCode(); - } - } - - protected class DefaultIndex extends AbstractIndex { - - public DefaultIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenHashMap<>(); - } - } - - protected class BooleanIndex extends AbstractIndex { - - public BooleanIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenHashMap<>(); - } - } - - protected class DoubleIndex extends AbstractIndex { - - public DoubleIndex(ColumnImpl column) { - super(column); - - map = new Double2ObjectAVLTreeMap<>(); - } - } - - protected class IntegerIndex extends AbstractIndex { - - public IntegerIndex(ColumnImpl column) { - super(column); - - map = new Int2ObjectAVLTreeMap<>(); - } - } - - protected class FloatIndex extends AbstractIndex { - - public FloatIndex(ColumnImpl column) { - super(column); - - map = new Float2ObjectAVLTreeMap<>(); - } - } - - protected class LongIndex extends AbstractIndex { - - public LongIndex(ColumnImpl column) { - super(column); - - map = new Long2ObjectAVLTreeMap<>(); - } - } - - protected class ShortIndex extends AbstractIndex { - - public ShortIndex(ColumnImpl column) { - super(column); - - map = new Short2ObjectAVLTreeMap<>(); - } - } - - protected class ByteIndex extends AbstractIndex { - - public ByteIndex(ColumnImpl column) { - super(column); - - map = new Byte2ObjectAVLTreeMap<>(); - } - } - - protected class GenericNumberIndex extends AbstractIndex { - - public GenericNumberIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectAVLTreeMap<>(); - } - } - - protected class CharIndex extends AbstractIndex { - - public CharIndex(ColumnImpl column) { - super(column); - - map = new Char2ObjectAVLTreeMap<>(); - } - } - - protected class DefaultArrayIndex extends AbstractIndex { - - public DefaultArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(ObjectArrays.HASH_STRATEGY); - } - } - - protected class BooleanArrayIndex extends AbstractIndex { - - public BooleanArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(BooleanArrays.HASH_STRATEGY); - } - } - - protected class DoubleArrayIndex extends AbstractIndex { - - public DoubleArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(DoubleArrays.HASH_STRATEGY); - } - } - - protected class IntegerArrayIndex extends AbstractIndex { - - public IntegerArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(IntArrays.HASH_STRATEGY); - } - } - - protected class FloatArrayIndex extends AbstractIndex { - - public FloatArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(FloatArrays.HASH_STRATEGY); - } - } - - protected class LongArrayIndex extends AbstractIndex { - - public LongArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(LongArrays.HASH_STRATEGY); - } - } - - protected class ShortArrayIndex extends AbstractIndex { - - public ShortArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(ShortArrays.HASH_STRATEGY); - } - } - - protected class ByteArrayIndex extends AbstractIndex { - - public ByteArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(ByteArrays.HASH_STRATEGY); - } - } - - protected class CharArrayIndex extends AbstractIndex { - - public CharArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(CharArrays.HASH_STRATEGY); - } - } - private class LockableIterable implements Iterable { private final Iterable ite; diff --git a/src/test/java/org/gephi/graph/impl/IndexImplTest.java b/src/test/java/org/gephi/graph/impl/IndexImplTest.java index 9365ee8e..73e9bc58 100644 --- a/src/test/java/org/gephi/graph/impl/IndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexImplTest.java @@ -314,7 +314,7 @@ public void testWithNullDecorator() { index.put(fooColumn, null, n1); index.put(fooColumn, "bar", n3); - IndexImpl.AbstractIndex withNullIndex = index.getIndex("foo"); + ColumnIndex withNullIndex = index.getIndex("foo"); Collection withNullCollection = withNullIndex.values(); Assert.assertEquals(withNullCollection.size(), 2); Assert.assertFalse(withNullCollection.isEmpty()); @@ -332,7 +332,7 @@ public void testWithNullDecorator() { Assert.assertEquals(withNullItr.next(), "bar"); Assert.assertFalse(withNullItr.hasNext()); - IndexImpl.AbstractIndex withoutNullIndex = index.getIndex("age"); + ColumnIndex withoutNullIndex = index.getIndex("age"); Collection withoutNullCollection = withoutNullIndex.values(); Assert.assertEquals(withoutNullCollection.size(), 2); Assert.assertFalse(withoutNullCollection.isEmpty()); From 7ffd50fb283bfbade51846254d98035eed8de7ae Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Dec 2021 13:07:36 +0100 Subject: [PATCH 039/271] Extract ColumnIndex to the API level --- .../java/org/gephi/graph/api/ColumnIndex.java | 87 +++++++ ...{ColumnIndex.java => ColumnIndexImpl.java} | 188 +++++++++++++--- .../org/gephi/graph/impl/ColumnStore.java | 2 +- .../java/org/gephi/graph/impl/IndexImpl.java | 213 ++++++------------ .../org/gephi/graph/impl/IndexImplTest.java | 4 +- .../org/gephi/graph/impl/IndexStoreTest.java | 6 +- 6 files changed, 309 insertions(+), 191 deletions(-) create mode 100644 src/main/java/org/gephi/graph/api/ColumnIndex.java rename src/main/java/org/gephi/graph/impl/{ColumnIndex.java => ColumnIndexImpl.java} (81%) diff --git a/src/main/java/org/gephi/graph/api/ColumnIndex.java b/src/main/java/org/gephi/graph/api/ColumnIndex.java new file mode 100644 index 00000000..004966d7 --- /dev/null +++ b/src/main/java/org/gephi/graph/api/ColumnIndex.java @@ -0,0 +1,87 @@ +package org.gephi.graph.api; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * A column index is associated with a column and and keeps track of each unique + * value and can also return the minimum and maximum values in case of a + * sortable value type. + * + * @param value type + * @param Element class + */ +public interface ColumnIndex extends Iterable>> { + + /** + * Counts the elements with value. + * + * @param value the value + * @return the number of elements in the column index with value, or + * zero if none + */ + int count(K value); + + /** + * Gets an Iterable of all elements in the column index with value. + * + * @param value the value + * @return an iterable with element with value + */ + Iterable get(K value); + + /** + * Returns all unique values. + * + * @return a collection of all unique values + */ + Collection values(); + + /** + * Counts the unique values. + * + * @return the number of distinct values. + */ + int countValues(); + + /** + * Counts the elements. + * + * @return the number of elements in column + */ + int countElements(); + + /** + * Returns whether the column index is numeric and sortable, and therefore + * methods {@link #getMinValue()} and {@link #getMaxValue()} are available. + * + * @return true if sortable, false otherwise + */ + boolean isSortable(); + + /** + * Returns the minimum value. + *

+ * Only applies for sortable indices. + * + * @return the minimum value + */ + Number getMinValue(); + + /** + * Returns the maximum value. + *

+ * Only applies for sortable indices. + * + * @return the maximum value + */ + Number getMaxValue(); + + /** + * Returns the column for which this column index belongs to. + * + * @return the column + */ + Column getColumn(); +} diff --git a/src/main/java/org/gephi/graph/impl/ColumnIndex.java b/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java similarity index 81% rename from src/main/java/org/gephi/graph/impl/ColumnIndex.java rename to src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java index bbe706d6..28f4216b 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnIndex.java +++ b/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java @@ -20,18 +20,22 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.shorts.Short2ObjectAVLTreeMap; import it.unimi.dsi.fastutil.shorts.ShortArrays; +import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.SortedMap; +import org.gephi.graph.api.ColumnIndex; import org.gephi.graph.api.Element; -public abstract class ColumnIndex implements Iterable>> { +public abstract class ColumnIndexImpl implements ColumnIndex { // Const public static final boolean TRIMMING_ENABLED = false; public static final int TRIMMING_FREQUENCY = 30; + // Lock (optional) + protected final TableLockImpl lock; // Data protected final ColumnImpl column; protected final ValueSet nullSet; @@ -39,12 +43,17 @@ public abstract class ColumnIndex implements Iterable(null); + if (column.table != null) { + lock = column.table.getLock(); + } else { + lock = null; + } } - public K putValue(T element, K value) { + protected K putValue(T element, K value) { if (value == null) { if (nullSet.add(element)) { elements++; @@ -63,7 +72,7 @@ public K putValue(T element, K value) { return value; } - public void removeValue(T element, K value) { + protected void removeValue(T element, K value) { if (value == null) { if (nullSet.remove(element)) { elements--; @@ -79,31 +88,64 @@ public void removeValue(T element, K value) { } } - public K replaceValue(T element, K oldValue, K newValue) { + protected K replaceValue(T element, K oldValue, K newValue) { removeValue(element, oldValue); return putValue(element, newValue); } - public int getCount(K value) { - if (value == null) { - return nullSet.size(); - } - ValueSet valueSet = getValueSet(value); - if (valueSet != null) { - return valueSet.size(); - } else { - return 0; + protected int getCount(K value) { + lock(); + try { + if (value == null) { + return nullSet.size(); + } + ValueSet valueSet = getValueSet(value); + if (valueSet != null) { + return valueSet.size(); + } else { + return 0; + } + } finally { + unlock(); } } - public Collection values() { - return new WithNullDecorator(); + @Override + public int count(K value) { + return getCount(value); + } + + @Override + public Collection values() { + lock(); + try { + return new ArrayList<>(new WithNullDecorator()); + } finally { + unlock(); + } } + @Override public int countValues() { - return (nullSet.isEmpty() ? 0 : 1) + map.size(); + lock(); + try { + return (nullSet.isEmpty() ? 0 : 1) + map.size(); + } finally { + unlock(); + } } + @Override + public int countElements() { + lock(); + try { + return elements; + } finally { + unlock(); + } + } + + @Override public Number getMinValue() { if (isSortable()) { if (map.isEmpty()) { @@ -117,6 +159,7 @@ public Number getMinValue() { } } + @Override public Number getMaxValue() { if (isSortable()) { if (map.isEmpty()) { @@ -147,6 +190,16 @@ protected void clear() { return new EntryIterator(); } + @Override + public Iterable get(K value) { + lock(); + ValueSet valueSet = getValueSet(value); + if (valueSet == null) { + return ValueSet.EMPTY; + } + return new LockableIterable<>(valueSet.set); + } + protected ValueSet getValueSet(K value) { if (value == null) { return nullSet; @@ -164,11 +217,29 @@ protected ValueSet addValue(K value) { return valueSet; } - protected boolean isSortable() { + @Override + public boolean isSortable() { return Number.class.isAssignableFrom(column.getTypeClass()) && map instanceof SortedMap; } - protected static class DefaultIndex extends ColumnIndex { + @Override + public ColumnImpl getColumn() { + return column; + } + + void lock() { + if (lock != null) { + lock.lock(); + } + } + + void unlock() { + if (lock != null) { + lock.unlock(); + } + } + + protected static class DefaultIndex extends ColumnIndexImpl { public DefaultIndex(ColumnImpl column) { super(column); @@ -177,7 +248,7 @@ public DefaultIndex(ColumnImpl column) { } } - protected static class BooleanIndex extends ColumnIndex { + protected static class BooleanIndex extends ColumnIndexImpl { public BooleanIndex(ColumnImpl column) { super(column); @@ -186,7 +257,7 @@ public BooleanIndex(ColumnImpl column) { } } - protected static class DoubleIndex extends ColumnIndex { + protected static class DoubleIndex extends ColumnIndexImpl { public DoubleIndex(ColumnImpl column) { super(column); @@ -195,7 +266,7 @@ public DoubleIndex(ColumnImpl column) { } } - protected static class IntegerIndex extends ColumnIndex { + protected static class IntegerIndex extends ColumnIndexImpl { public IntegerIndex(ColumnImpl column) { super(column); @@ -204,7 +275,7 @@ public IntegerIndex(ColumnImpl column) { } } - protected static class FloatIndex extends ColumnIndex { + protected static class FloatIndex extends ColumnIndexImpl { public FloatIndex(ColumnImpl column) { super(column); @@ -213,7 +284,7 @@ public FloatIndex(ColumnImpl column) { } } - protected static class LongIndex extends ColumnIndex { + protected static class LongIndex extends ColumnIndexImpl { public LongIndex(ColumnImpl column) { super(column); @@ -222,7 +293,7 @@ public LongIndex(ColumnImpl column) { } } - protected static class ShortIndex extends ColumnIndex { + protected static class ShortIndex extends ColumnIndexImpl { public ShortIndex(ColumnImpl column) { super(column); @@ -231,7 +302,7 @@ public ShortIndex(ColumnImpl column) { } } - protected static class ByteIndex extends ColumnIndex { + protected static class ByteIndex extends ColumnIndexImpl { public ByteIndex(ColumnImpl column) { super(column); @@ -240,7 +311,7 @@ public ByteIndex(ColumnImpl column) { } } - protected static class GenericNumberIndex extends ColumnIndex { + protected static class GenericNumberIndex extends ColumnIndexImpl { public GenericNumberIndex(ColumnImpl column) { super(column); @@ -249,7 +320,7 @@ public GenericNumberIndex(ColumnImpl column) { } } - protected static class CharIndex extends ColumnIndex { + protected static class CharIndex extends ColumnIndexImpl { public CharIndex(ColumnImpl column) { super(column); @@ -258,7 +329,7 @@ public CharIndex(ColumnImpl column) { } } - protected static class DefaultArrayIndex extends ColumnIndex { + protected static class DefaultArrayIndex extends ColumnIndexImpl { public DefaultArrayIndex(ColumnImpl column) { super(column); @@ -267,7 +338,7 @@ public DefaultArrayIndex(ColumnImpl column) { } } - protected static class BooleanArrayIndex extends ColumnIndex { + protected static class BooleanArrayIndex extends ColumnIndexImpl { public BooleanArrayIndex(ColumnImpl column) { super(column); @@ -276,7 +347,7 @@ public BooleanArrayIndex(ColumnImpl column) { } } - protected static class DoubleArrayIndex extends ColumnIndex { + protected static class DoubleArrayIndex extends ColumnIndexImpl { public DoubleArrayIndex(ColumnImpl column) { super(column); @@ -285,7 +356,7 @@ public DoubleArrayIndex(ColumnImpl column) { } } - protected static class IntegerArrayIndex extends ColumnIndex { + protected static class IntegerArrayIndex extends ColumnIndexImpl { public IntegerArrayIndex(ColumnImpl column) { super(column); @@ -294,7 +365,7 @@ public IntegerArrayIndex(ColumnImpl column) { } } - protected static class FloatArrayIndex extends ColumnIndex { + protected static class FloatArrayIndex extends ColumnIndexImpl { public FloatArrayIndex(ColumnImpl column) { super(column); @@ -303,7 +374,7 @@ public FloatArrayIndex(ColumnImpl column) { } } - protected static class LongArrayIndex extends ColumnIndex { + protected static class LongArrayIndex extends ColumnIndexImpl { public LongArrayIndex(ColumnImpl column) { super(column); @@ -312,7 +383,7 @@ public LongArrayIndex(ColumnImpl column) { } } - protected static class ShortArrayIndex extends ColumnIndex { + protected static class ShortArrayIndex extends ColumnIndexImpl { public ShortArrayIndex(ColumnImpl column) { super(column); @@ -321,7 +392,7 @@ public ShortArrayIndex(ColumnImpl column) { } } - protected static class ByteArrayIndex extends ColumnIndex { + protected static class ByteArrayIndex extends ColumnIndexImpl { public ByteArrayIndex(ColumnImpl column) { super(column); @@ -330,7 +401,7 @@ public ByteArrayIndex(ColumnImpl column) { } } - protected static class CharArrayIndex extends ColumnIndex { + protected static class CharArrayIndex extends ColumnIndexImpl { public CharArrayIndex(ColumnImpl column) { super(column); @@ -341,6 +412,7 @@ public CharArrayIndex(ColumnImpl column) { protected static final class ValueSet implements Set { + protected static ValueSet EMPTY = new ValueSet(null); protected final K value; private final Set set; @@ -613,4 +685,46 @@ public Set setValue(Set v) { throw new UnsupportedOperationException("Not supported operation."); } } + + private class LockableIterable implements Iterable { + + private final Iterable ite; + + public LockableIterable(Iterable ite) { + this.ite = ite; + } + + @Override + public Iterator iterator() { + return new LockableIterator<>(ite.iterator()); + } + } + + private class LockableIterator implements Iterator { + + private final Iterator itr; + + public LockableIterator(Iterator itr) { + this.itr = itr; + } + + @Override + public boolean hasNext() { + boolean n = itr.hasNext(); + if (!n && lock != null) { + lock.unlock(); + } + return n; + } + + @Override + public T next() { + return itr.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } + } } diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index 8c4ad072..f064385a 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -220,7 +220,7 @@ public Column getColumnByIndex(final int index) { } } - public Column getColumn(final String key) { + public ColumnImpl getColumn(final String key) { checkNonNullObject(key); lock(); try { diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index f6bc21d2..491c67ac 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -13,47 +13,26 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; -import it.unimi.dsi.fastutil.booleans.BooleanArrays; -import it.unimi.dsi.fastutil.bytes.Byte2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.bytes.ByteArrays; -import it.unimi.dsi.fastutil.chars.Char2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.chars.CharArrays; -import it.unimi.dsi.fastutil.doubles.Double2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.doubles.DoubleArrays; -import it.unimi.dsi.fastutil.floats.Float2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.floats.FloatArrays; -import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.ints.IntArrays; -import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.longs.LongArrays; -import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrays; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import it.unimi.dsi.fastutil.shorts.Short2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.shorts.ShortArrays; -import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.Map; import java.util.Set; import org.gephi.graph.api.Column; -import org.gephi.graph.api.Index; import org.gephi.graph.api.Element; +import org.gephi.graph.api.Index; public class IndexImpl implements Index { protected final TableLockImpl lock; protected final ColumnStore columnStore; - protected ColumnIndex[] columns; + protected ColumnIndexImpl[] columns; protected int columnsCount; public IndexImpl(ColumnStore columnStore) { this.columnStore = columnStore; - this.columns = new ColumnIndex[0]; + this.columns = new ColumnIndexImpl[0]; this.lock = columnStore.lock; } @@ -73,8 +52,7 @@ public int count(Column column, Object value) { lock(); try { - ColumnIndex index = getIndex((ColumnImpl) column); - return index.getCount(value); + return getIndex(column).getCount(value); } finally { unlock(); } @@ -83,29 +61,25 @@ public int count(Column column, Object value) { public int count(String key, Object value) { checkNonNullObject(key); - ColumnIndex index = getIndex(key); - return index.getCount(value); - } - - public Iterable get(String key, Object value) { - checkNonNullObject(key); - - ColumnIndex index = getIndex(key); - return index.getValueSet(value); + return count(columnStore.getColumn(key), value); } @Override public Iterable get(Column column, Object value) { checkNonNullColumnObject(column); - if (lock != null) { - lock.lock(); - ColumnIndex index = getIndex((ColumnImpl) column); - Set valueSet = index.getValueSet(value); - return valueSet == null ? null : new LockableIterable<>(index.getValueSet(value)); + lock(); + try { + return getIndex(column).get(value); + } finally { + unlock(); } - ColumnIndex index = getIndex((ColumnImpl) column); - return index.getValueSet(value); + } + + public Iterable get(String key, Object value) { + checkNonNullObject(key); + + return get(columnStore.getColumn(key), value); } @Override @@ -114,9 +88,7 @@ public boolean isSortable(Column column) { lock(); try { - ColumnIndex index = getIndex((ColumnImpl) column); - - return index.isSortable(); + return getIndex(column).isSortable(); } finally { unlock(); } @@ -128,8 +100,7 @@ public Number getMinValue(Column column) { lock(); try { - ColumnIndex index = getIndex((ColumnImpl) column); - return index.getMinValue(); + return getIndex(column).getMinValue(); } finally { unlock(); } @@ -140,8 +111,7 @@ public Number getMaxValue(Column column) { checkNonNullColumnObject(column); lock(); try { - ColumnIndex index = getIndex((ColumnImpl) column); - return index.getMaxValue(); + return getIndex(column).getMaxValue(); } finally { unlock(); } @@ -150,8 +120,7 @@ public Number getMaxValue(Column column) { public Iterable>> get(Column column) { checkNonNullColumnObject(column); - ColumnIndex index = getIndex((ColumnImpl) column); - return index; + return getIndex((ColumnImpl) column); } @Override @@ -160,8 +129,7 @@ public Collection values(Column column) { lock(); try { - ColumnIndex index = getIndex((ColumnImpl) column); - return new ArrayList(index.values()); + return getIndex(column).values(); } finally { unlock(); } @@ -172,8 +140,7 @@ public int countValues(Column column) { checkNonNullColumnObject(column); lock(); try { - ColumnIndex index = getIndex((ColumnImpl) column); - return index.countValues(); + return getIndex(column).countValues(); } finally { unlock(); } @@ -184,8 +151,7 @@ public int countElements(Column column) { checkNonNullColumnObject(column); lock(); try { - ColumnIndex index = getIndex((ColumnImpl) column); - return index.elements; + return getIndex(column).elements; } finally { unlock(); } @@ -194,47 +160,41 @@ public int countElements(Column column) { public Object put(String key, Object value, T element) { checkNonNullObject(key); - ColumnIndex index = getIndex(key); - return index.putValue(element, value); + return put(columnStore.getColumn(key), value, element); } public Object put(Column column, Object value, T element) { checkNonNullColumnObject(column); - ColumnIndex index = getIndex((ColumnImpl) column); - return index.putValue(element, value); + return getIndex(column).putValue(element, value); } public void remove(String key, Object value, T element) { checkNonNullObject(key); - ColumnIndex index = getIndex(key); - index.removeValue(element, value); + remove(columnStore.getColumn(key), value, element); } public void remove(Column column, Object value, T element) { checkNonNullColumnObject(column); - ColumnIndex index = getIndex((ColumnImpl) column); - index.removeValue(element, value); + getIndex(column).removeValue(element, value); } public Object set(String key, Object oldValue, Object value, T element) { checkNonNullObject(key); - ColumnIndex index = getIndex(key); - return index.replaceValue(element, oldValue, value); + return set(columnStore.getColumn(key), oldValue, value, element); } public Object set(Column column, Object oldValue, Object value, T element) { checkNonNullColumnObject(column); - ColumnIndex index = getIndex((ColumnImpl) column); - return index.replaceValue(element, oldValue, value); + return getIndex(column).replaceValue(element, oldValue, value); } public void clear() { - for (ColumnIndex ai : columns) { + for (ColumnIndexImpl ai : columns) { if (ai != null) { ai.clear(); } @@ -244,7 +204,7 @@ public void clear() { protected void addColumn(ColumnImpl col) { if (col.isIndexed()) { ensureColumnSize(col.storeId); - ColumnIndex index = createIndex(col); + ColumnIndexImpl index = createIndex(col); columns[col.storeId] = index; columnsCount++; } @@ -254,7 +214,7 @@ protected void addAllColumns(ColumnImpl[] cols) { ensureColumnSize(cols.length); for (ColumnImpl col : cols) { if (col.isIndexed()) { - ColumnIndex index = createIndex(col); + ColumnIndexImpl index = createIndex(col); columns[col.storeId] = index; columnsCount++; } @@ -263,7 +223,7 @@ protected void addAllColumns(ColumnImpl[] cols) { protected void removeColumn(ColumnImpl col) { if (col.isIndexed()) { - ColumnIndex index = columns[col.storeId]; + ColumnIndexImpl index = columns[col.storeId]; index.destroy(); columns[col.storeId] = null; columnsCount--; @@ -280,11 +240,11 @@ protected boolean hasColumn(ColumnImpl col) { return false; } - protected ColumnIndex getIndex(ColumnImpl col) { + protected ColumnIndexImpl getIndex(Column col) { if (col.isIndexed()) { - int id = col.storeId; + int id = col.getIndex(); if (id != ColumnStore.NULL_ID && columns.length > id) { - ColumnIndex index = columns[id]; + ColumnIndexImpl index = columns[id]; if (index != null && index.column == col) { return index; } @@ -293,21 +253,17 @@ protected ColumnIndex getIndex(ColumnImpl col) { return null; } - protected ColumnIndex getIndex(String key) { - int id = columnStore.getColumnIndex(key); - if (id != ColumnStore.NULL_ID && columns.length > id) { - return columns[id]; - } - return null; + protected ColumnIndexImpl getIndex(String key) { + return getIndex(columnStore.getColumn(key)); } protected void destroy() { - for (ColumnIndex ai : columns) { + for (ColumnIndexImpl ai : columns) { if (ai != null) { ai.destroy(); } } - columns = new ColumnIndex[0]; + columns = new ColumnIndexImpl[0]; columnsCount = 0; } @@ -315,74 +271,74 @@ protected int size() { return columnsCount; } - ColumnIndex createIndex(ColumnImpl column) { + ColumnIndexImpl createIndex(ColumnImpl column) { if (column.getTypeClass().equals(Byte.class)) { // Byte - return new ColumnIndex.ByteIndex(column); + return new ColumnIndexImpl.ByteIndex(column); } else if (column.getTypeClass().equals(Short.class)) { // Short - return new ColumnIndex.ShortIndex(column); + return new ColumnIndexImpl.ShortIndex(column); } else if (column.getTypeClass().equals(Integer.class)) { // Integer - return new ColumnIndex.IntegerIndex(column); + return new ColumnIndexImpl.IntegerIndex(column); } else if (column.getTypeClass().equals(Long.class)) { // Long - return new ColumnIndex.LongIndex(column); + return new ColumnIndexImpl.LongIndex(column); } else if (column.getTypeClass().equals(Float.class)) { // Float - return new ColumnIndex.FloatIndex(column); + return new ColumnIndexImpl.FloatIndex(column); } else if (column.getTypeClass().equals(Double.class)) { // Double - return new ColumnIndex.DoubleIndex(column); + return new ColumnIndexImpl.DoubleIndex(column); } else if (Number.class.isAssignableFrom(column.getTypeClass())) { // Other numbers - return new ColumnIndex.GenericNumberIndex(column); + return new ColumnIndexImpl.GenericNumberIndex(column); } else if (column.getTypeClass().equals(Boolean.class)) { // Boolean - return new ColumnIndex.BooleanIndex(column); + return new ColumnIndexImpl.BooleanIndex(column); } else if (column.getTypeClass().equals(Character.class)) { // Char - return new ColumnIndex.CharIndex(column); + return new ColumnIndexImpl.CharIndex(column); } else if (column.getTypeClass().equals(String.class)) { // String - return new ColumnIndex.DefaultIndex(column); + return new ColumnIndexImpl.DefaultIndex(column); } else if (column.getTypeClass().equals(byte[].class)) { // Byte Array - return new ColumnIndex.ByteArrayIndex(column); + return new ColumnIndexImpl.ByteArrayIndex(column); } else if (column.getTypeClass().equals(short[].class)) { // Short Array - return new ColumnIndex.ShortArrayIndex(column); + return new ColumnIndexImpl.ShortArrayIndex(column); } else if (column.getTypeClass().equals(int[].class)) { // Integer Array - return new ColumnIndex.IntegerArrayIndex(column); + return new ColumnIndexImpl.IntegerArrayIndex(column); } else if (column.getTypeClass().equals(long[].class)) { // Long Array - return new ColumnIndex.LongArrayIndex(column); + return new ColumnIndexImpl.LongArrayIndex(column); } else if (column.getTypeClass().equals(float[].class)) { // Float array - return new ColumnIndex.FloatArrayIndex(column); + return new ColumnIndexImpl.FloatArrayIndex(column); } else if (column.getTypeClass().equals(double[].class)) { // Double array - return new ColumnIndex.DoubleArrayIndex(column); + return new ColumnIndexImpl.DoubleArrayIndex(column); } else if (column.getTypeClass().equals(boolean[].class)) { // Boolean array - return new ColumnIndex.BooleanArrayIndex(column); + return new ColumnIndexImpl.BooleanArrayIndex(column); } else if (column.getTypeClass().equals(char[].class)) { // Char array - return new ColumnIndex.CharArrayIndex(column); + return new ColumnIndexImpl.CharArrayIndex(column); } else if (column.getTypeClass().equals(String[].class)) { // String array - return new ColumnIndex.DefaultArrayIndex(column); + return new ColumnIndexImpl.DefaultArrayIndex(column); } else if (column.getTypeClass().isArray()) { // Default Array - return new ColumnIndex.DefaultArrayIndex(column); + return new ColumnIndexImpl.DefaultArrayIndex(column); } - return new ColumnIndex.DefaultIndex(column); + return new ColumnIndexImpl.DefaultIndex(column); } private void ensureColumnSize(int index) { if (index >= columns.length) { - ColumnIndex[] newArray = new ColumnIndex[index + 1]; + ColumnIndexImpl[] newArray = new ColumnIndexImpl[index + 1]; System.arraycopy(columns, 0, newArray, 0, columns.length); columns = newArray; } @@ -415,45 +371,4 @@ void checkNonNullColumnObject(final Object o) { } } - private class LockableIterable implements Iterable { - - private final Iterable ite; - - public LockableIterable(Iterable ite) { - this.ite = ite; - } - - @Override - public Iterator iterator() { - return new LockableIterator<>(ite.iterator()); - } - } - - private class LockableIterator implements Iterator { - - private final Iterator itr; - - public LockableIterator(Iterator itr) { - this.itr = itr; - } - - @Override - public boolean hasNext() { - boolean n = itr.hasNext(); - if (!n) { - lock.unlock(); - } - return n; - } - - @Override - public T next() { - return itr.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Not supported."); - } - } } diff --git a/src/test/java/org/gephi/graph/impl/IndexImplTest.java b/src/test/java/org/gephi/graph/impl/IndexImplTest.java index 73e9bc58..80f8b689 100644 --- a/src/test/java/org/gephi/graph/impl/IndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexImplTest.java @@ -314,7 +314,7 @@ public void testWithNullDecorator() { index.put(fooColumn, null, n1); index.put(fooColumn, "bar", n3); - ColumnIndex withNullIndex = index.getIndex("foo"); + ColumnIndexImpl withNullIndex = index.getIndex("foo"); Collection withNullCollection = withNullIndex.values(); Assert.assertEquals(withNullCollection.size(), 2); Assert.assertFalse(withNullCollection.isEmpty()); @@ -332,7 +332,7 @@ public void testWithNullDecorator() { Assert.assertEquals(withNullItr.next(), "bar"); Assert.assertFalse(withNullItr.hasNext()); - ColumnIndex withoutNullIndex = index.getIndex("age"); + ColumnIndexImpl withoutNullIndex = index.getIndex("age"); Collection withoutNullCollection = withoutNullIndex.values(); Assert.assertEquals(withoutNullCollection.size(), 2); Assert.assertFalse(withoutNullCollection.isEmpty()); diff --git a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java index 3943230e..193b472e 100644 --- a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java @@ -102,10 +102,12 @@ public void testIndexNode() { Assert.assertTrue(mainIndex.values(col2).contains(20)); Assert.assertSame(getIterable(mainIndex.get(col1, "A"))[0], n); - Assert.assertNull(mainIndex.get(col1, "B")); + Assert.assertNotNull(mainIndex.get(col1, "B")); + Assert.assertFalse(mainIndex.get(col1, "B").iterator().hasNext()); Assert.assertSame(getIterable(mainIndex.get("foo", "A"))[0], n); - Assert.assertNull(mainIndex.get("foo", "B")); + Assert.assertNotNull(mainIndex.get("foo", "B")); + Assert.assertFalse(mainIndex.get("foo", "B").iterator().hasNext()); } @Test From eb0b1efe7eb7cac204091b4d316b65f5dcd17cbf Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Dec 2021 13:12:20 +0100 Subject: [PATCH 040/271] Add getColumnIndex to the Index interface --- src/main/java/org/gephi/graph/api/Index.java | 11 ++++++++++- src/main/java/org/gephi/graph/impl/IndexImpl.java | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/gephi/graph/api/Index.java b/src/main/java/org/gephi/graph/api/Index.java index a0de16c4..0e563548 100644 --- a/src/main/java/org/gephi/graph/api/Index.java +++ b/src/main/java/org/gephi/graph/api/Index.java @@ -21,7 +21,8 @@ * An index is associated with each table and keeps track of each unique value * in indexed columns. *

- * + * Each column is associated with a @{{@link ColumnIndex}}. + * * @param Element class */ public interface Index { @@ -115,4 +116,12 @@ public interface Index { * @return the index name */ public String getIndexName(); + + /** + * Returns the column index for the given column. + * + * @param column the column to get the index for + * @return the column index + */ + public ColumnIndex getColumnIndex(Column column); } diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index 491c67ac..c68f7ff3 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Set; import org.gephi.graph.api.Column; +import org.gephi.graph.api.ColumnIndex; import org.gephi.graph.api.Element; import org.gephi.graph.api.Index; @@ -46,6 +47,11 @@ public String getIndexName() { return "index_" + columnStore.elementType.getCanonicalName(); } + @Override + public ColumnIndex getColumnIndex(Column column) { + return getIndex(column); + } + @Override public int count(Column column, Object value) { checkNonNullColumnObject(column); From 1a9eda6098048445fad3b2282b3cab0199019b01 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Dec 2021 14:12:25 +0100 Subject: [PATCH 041/271] Add missing license headers --- .../java/org/gephi/graph/api/ColumnIndex.java | 15 +++++++++++++++ .../java/org/gephi/graph/api/GraphLock.java | 15 +++++++++++++++ src/main/java/org/gephi/graph/api/Rect2D.java | 15 +++++++++++++++ .../java/org/gephi/graph/api/SpatialIndex.java | 17 +++++++++++++++-- .../java/org/gephi/graph/api/TableLock.java | 15 +++++++++++++++ .../org/gephi/graph/impl/ColumnIndexImpl.java | 15 +++++++++++++++ .../gephi/graph/impl/EdgeIterableWrapper.java | 15 +++++++++++++++ .../graph/impl/ElementIterableWrapper.java | 15 +++++++++++++++ .../gephi/graph/impl/NodeIterableWrapper.java | 15 +++++++++++++++ 9 files changed, 135 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/ColumnIndex.java b/src/main/java/org/gephi/graph/api/ColumnIndex.java index 004966d7..3c0e4088 100644 --- a/src/main/java/org/gephi/graph/api/ColumnIndex.java +++ b/src/main/java/org/gephi/graph/api/ColumnIndex.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.api; import java.util.Collection; diff --git a/src/main/java/org/gephi/graph/api/GraphLock.java b/src/main/java/org/gephi/graph/api/GraphLock.java index 457cf8d1..342f1253 100644 --- a/src/main/java/org/gephi/graph/api/GraphLock.java +++ b/src/main/java/org/gephi/graph/api/GraphLock.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.api; /** diff --git a/src/main/java/org/gephi/graph/api/Rect2D.java b/src/main/java/org/gephi/graph/api/Rect2D.java index f7d89025..53f909ad 100644 --- a/src/main/java/org/gephi/graph/api/Rect2D.java +++ b/src/main/java/org/gephi/graph/api/Rect2D.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.api; import java.text.DecimalFormat; diff --git a/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java index ee41ddc5..633a1356 100644 --- a/src/main/java/org/gephi/graph/api/SpatialIndex.java +++ b/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -1,7 +1,20 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.api; -import java.util.function.Consumer; - /** * Object to query the nodes and edges of the graph in a spatial context. * diff --git a/src/main/java/org/gephi/graph/api/TableLock.java b/src/main/java/org/gephi/graph/api/TableLock.java index 0f342a0d..52b2015f 100644 --- a/src/main/java/org/gephi/graph/api/TableLock.java +++ b/src/main/java/org/gephi/graph/api/TableLock.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.api; public interface TableLock { diff --git a/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java index 28f4216b..73cdfcd6 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; import it.unimi.dsi.fastutil.booleans.BooleanArrays; diff --git a/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java b/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java index 4cf675dd..8db89823 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; import java.util.Iterator; diff --git a/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java b/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java index 88d1f2c4..d4650934 100644 --- a/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; import java.util.ArrayList; diff --git a/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java b/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java index 8961d327..d8cb4c79 100644 --- a/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; import java.util.Iterator; From 90f08197ac5cf8918443b32a017e7482e2c0a375 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Dec 2021 20:29:12 +0100 Subject: [PATCH 042/271] Add ColumnNoIndex implementation for #141 --- .../gephi/graph/impl/ColumnNoIndexImpl.java | 220 ++++++++++++++++++ .../java/org/gephi/graph/impl/IndexImpl.java | 2 +- .../java/org/gephi/graph/impl/TableImpl.java | 2 +- .../gephi/graph/impl/ColumnNoIndexTest.java | 190 +++++++++++++++ 4 files changed, 412 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java create mode 100644 src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java diff --git a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java new file mode 100644 index 00000000..691cc580 --- /dev/null +++ b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java @@ -0,0 +1,220 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import org.gephi.graph.api.Column; +import org.gephi.graph.api.ColumnIndex; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Element; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.Node; + +public class ColumnNoIndexImpl implements ColumnIndex { + + // Data + protected final ColumnImpl column; + // Stores + protected final Class elementClass; + // Graph + protected final Graph graph; + + protected ColumnNoIndexImpl(ColumnImpl column, Graph graph, Class elementClass) { + this.column = column; + this.elementClass = elementClass; + this.graph = graph; + } + + private Iterator getElementIterator() { + if (elementClass.equals(Node.class)) { + return (Iterator) graph.getNodes().iterator(); + } else if (elementClass.equals(Edge.class)) { + return (Iterator) graph.getEdges().iterator(); + } + return null; + } + + @Override + public int count(K value) { + Iterator elementIterator = getElementIterator(); + int count = 0; + if (elementIterator != null) { + while (elementIterator.hasNext()) { + ElementImpl element = (ElementImpl) elementIterator.next(); + K obj = (K) element.getAttribute(column); + if (value == null && obj == null) { + count++; + } else if (value != null && value.equals(obj)) { + count++; + } + } + } + return count; + } + + @Override + public Iterable get(K value) { + return new ElementWithValueIterable(getElementIterator(), value); + } + + @Override + public Collection values() { + Iterator elementIterator = getElementIterator(); + Set set = new ObjectOpenHashSet<>(); + if (elementIterator != null) { + while (elementIterator.hasNext()) { + ElementImpl element = (ElementImpl) elementIterator.next(); + K obj = (K) element.getAttribute(column); + set.add(obj); + } + } + return set; + } + + @Override + public int countValues() { + return values().size(); + } + + @Override + public int countElements() { + if (elementClass.equals(Node.class)) { + return graph.getNodeCount(); + } else if (elementClass.equals(Edge.class)) { + return graph.getEdgeCount(); + } + return 0; + } + + @Override + public boolean isSortable() { + return Number.class.isAssignableFrom(column.getTypeClass()); + } + + @Override + public Number getMinValue() { + if (!isSortable()) { + throw new UnsupportedOperationException("Only supported for sortable columns"); + } + Number min = null; + Iterator elementIterator = getElementIterator(); + if (elementIterator != null) { + double minN = Double.POSITIVE_INFINITY; + while (elementIterator.hasNext()) { + ElementImpl element = (ElementImpl) elementIterator.next(); + Number num = (Number) element.getAttribute(column); + if (min == null || (num != null && num.doubleValue() < minN)) { + if (num != null) { + minN = num.doubleValue(); + } + min = num; + } + } + } + return min; + } + + @Override + public Number getMaxValue() { + if (!isSortable()) { + throw new UnsupportedOperationException("Only supported for sortable columns"); + } + Number max = null; + Iterator elementIterator = getElementIterator(); + if (elementIterator != null) { + double maxN = Double.NEGATIVE_INFINITY; + + while (elementIterator.hasNext()) { + ElementImpl element = (ElementImpl) elementIterator.next(); + Number num = (Number) element.getAttribute(column); + if (max == null || (num != null && num.doubleValue() > maxN)) { + if (num != null) { + maxN = num.doubleValue(); + } + max = num; + } + } + } + return max; + } + + @Override + public Column getColumn() { + return column; + } + + @Override + public Iterator>> iterator() { + // TODO + throw new UnsupportedOperationException("Not implemented yet"); + } + + private class ElementWithValueIterable implements Iterable { + + private final Iterator ite; + private final K value; + + public ElementWithValueIterable(Iterator ite, K value) { + this.ite = ite; + this.value = value; + } + + @Override + public Iterator iterator() { + return new ElementWithValueIterator(ite, value); + } + } + + private class ElementWithValueIterator implements Iterator { + + private final Iterator itr; + private final K value; + private T pointer; + + public ElementWithValueIterator(Iterator itr, K value) { + this.itr = itr; + this.value = value; + } + + @Override + public boolean hasNext() { + while (pointer == null && itr.hasNext()) { + T element = itr.next(); + K val = (K) element.getAttribute(column); + if ((value == null && val == null) || (val != null && val.equals(value))) { + pointer = element; + } + } + return pointer != null; + } + + @Override + public T next() { + T res = pointer; + pointer = null; + return res; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } + } +} diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index c68f7ff3..5774dfe1 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -126,7 +126,7 @@ public Number getMaxValue(Column column) { public Iterable>> get(Column column) { checkNonNullColumnObject(column); - return getIndex((ColumnImpl) column); + return getIndex(column); } @Override diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index 679ad923..613a0862 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -147,7 +147,7 @@ public Column getColumn(int index) { } @Override - public Column getColumn(String id) { + public ColumnImpl getColumn(String id) { return store.getColumn(id.toLowerCase()); } diff --git a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java new file mode 100644 index 00000000..728e5419 --- /dev/null +++ b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java @@ -0,0 +1,190 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; + +import java.util.ArrayList; +import org.gephi.graph.api.Column; +import org.gephi.graph.api.Element; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.Origin; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +@Test(singleThreaded = true) +public class ColumnNoIndexTest { + + private GraphStore graphStore; + private ColumnNoIndexImpl fooIndex; + private ColumnNoIndexImpl ageIndex; + + @BeforeMethod + public void setup() { + graphStore = generateGraphStoreWithColumns(); + Column col = graphStore.nodeTable.getColumn("foo"); + Column col2 = graphStore.nodeTable.getColumn("age"); + + fooIndex = createIndex(graphStore, col.getId()); + ageIndex = createIndex(graphStore, col2.getId()); + } + + @AfterMethod + public void cleanUp() { + fooIndex = null; + ageIndex = null; + graphStore = null; + } + + @Test + public void testEmpty() { + ColumnNoIndexImpl index = createIndex(graphStore, "id"); + + Assert.assertTrue(index.values().isEmpty()); + Assert.assertEquals(index.countValues(), 0); + Assert.assertEquals(index.countElements(), 0); + Assert.assertFalse(index.isSortable()); + Assert.assertEquals(index.count(null), 0); + Assert.assertEquals(index.count("foo"), 0); + Assert.assertFalse(index.get(null).iterator().hasNext()); + Assert.assertFalse(index.get("foo").iterator().hasNext()); + } + + @Test + public void testCountsAdd() { + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "1", "bar"); + + Assert.assertEquals(fooIndex.countValues(), 1); + Assert.assertEquals(fooIndex.countElements(), 1); + + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "2", "bar"); + Assert.assertEquals(fooIndex.countValues(), 1); + Assert.assertEquals(fooIndex.countElements(), 2); + + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "3", "foo"); + Assert.assertEquals(fooIndex.countValues(), 2); + Assert.assertEquals(fooIndex.countElements(), 3); + } + + @Test + public void testCountsWithNulls() { + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "1", null); + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "2", null); + + Assert.assertEquals(fooIndex.countValues(), 1); + Assert.assertEquals(fooIndex.countElements(), 2); + } + + @Test + public void testCountsRemove() { + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "1", null); + graphStore.removeNode(graphStore.getNode("1")); + + Assert.assertEquals(fooIndex.countValues(), 0); + Assert.assertEquals(fooIndex.countElements(), 0); + } + + @Test + public void testCountByValue() { + Assert.assertEquals(fooIndex.count("bar"), 0); + Assert.assertEquals(fooIndex.count(null), 0); + + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "1", "bar"); + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "2", null); + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "3", "bar"); + + Assert.assertEquals(fooIndex.count("bar"), 2); + Assert.assertEquals(fooIndex.count(null), 1); + } + + @Test + public void testValues() { + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "1", "bar"); + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "2", null); + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "3", "bar"); + + Assert.assertEqualsNoOrder(fooIndex.values().toArray(new String[0]), new String[] { "bar", null }); + } + + @Test + public void testGet() { + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "1", "bar"); + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "2", null); + addNodeWithAttribute(graphStore, fooIndex.getColumn(), "3", "bar"); + + ArrayList res = new ArrayList<>(); + fooIndex.get("bar").forEach(res::add); + Assert.assertEqualsNoOrder(res + .toArray(new Node[0]), new Node[] { graphStore.getNode("1"), graphStore.getNode("3") }); + + ArrayList res2 = new ArrayList<>(); + fooIndex.get(null).forEach(res2::add); + Assert.assertEqualsNoOrder(res2.toArray(new Node[0]), new Node[] { graphStore.getNode("2") }); + } + + @Test + public void testIsSortable() { + Assert.assertFalse(fooIndex.isSortable()); + Assert.assertTrue(ageIndex.isSortable()); + } + + @Test + public void testGetMinMaxValueEmpty() { + Assert.assertNull(ageIndex.getMinValue()); + Assert.assertNull(ageIndex.getMaxValue()); + } + + @Test + public void testGetMinMaxValue() { + addNodeWithAttribute(graphStore, ageIndex.getColumn(), "1", 12); + addNodeWithAttribute(graphStore, ageIndex.getColumn(), "2", null); + addNodeWithAttribute(graphStore, ageIndex.getColumn(), "3", 6); + + Assert.assertEquals(ageIndex.getMinValue(), 6); + Assert.assertEquals(ageIndex.getMaxValue(), 12); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testGetMaxValueNotSortable() { + fooIndex.getMaxValue(); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testGetMinValueNotSortable() { + fooIndex.getMinValue(); + } + + private ColumnNoIndexImpl createIndex(GraphStore graphStore, String id) { + return new ColumnNoIndexImpl(graphStore.nodeTable.getColumn(id), graphStore, Node.class); + } + + private Node addNodeWithAttribute(GraphStore store, Column column, String id, Object val) { + Node node = store.factory.newNode(id); + node.setAttribute(column, val); + store.addNode(node); + return node; + } + + private GraphStore generateGraphStoreWithColumns() { + GraphStore graphStore = new GraphStore(); + ColumnStore columnStore = graphStore.nodeTable.store; + columnStore.addColumn(new ColumnImpl("foo", String.class, "foo", null, Origin.DATA, false, false)); + columnStore.addColumn(new ColumnImpl("age", Integer.class, "Age", null, Origin.DATA, true, false)); + + return graphStore; + } + +} From 6b07cc0149d13e9b8304f20f565134be9f0da189 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Dec 2021 20:53:49 +0100 Subject: [PATCH 043/271] Add more tests for Edge elements --- .../org/gephi/graph/impl/ColumnNoIndexTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java index 728e5419..72cdb954 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import org.gephi.graph.api.Column; +import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; @@ -63,6 +64,15 @@ public void testEmpty() { Assert.assertFalse(index.get("foo").iterator().hasNext()); } + @Test + public void testEmptyEdgeInstead() { + ColumnNoIndexImpl index = createEdgeIndex(graphStore, "id"); + + Assert.assertEquals(index.countElements(), 0); + Assert.assertTrue(index.values().isEmpty()); + Assert.assertFalse(index.get("foo").iterator().hasNext()); + } + @Test public void testCountsAdd() { addNodeWithAttribute(graphStore, fooIndex.getColumn(), "1", "bar"); @@ -171,6 +181,10 @@ private ColumnNoIndexImpl createIndex(GraphStore gr return new ColumnNoIndexImpl(graphStore.nodeTable.getColumn(id), graphStore, Node.class); } + private ColumnNoIndexImpl createEdgeIndex(GraphStore graphStore, String id) { + return new ColumnNoIndexImpl(graphStore.nodeTable.getColumn(id), graphStore, Edge.class); + } + private Node addNodeWithAttribute(GraphStore store, Column column, String id, Object val) { Node node = store.factory.newNode(id); node.setAttribute(column, val); From e9a35454ef1e693ae3b70c75c3b7aeb739d4bfea Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Dec 2021 21:21:08 +0100 Subject: [PATCH 044/271] Create intermediate ColumnIndexImpl interface to allow for generalization in IndexImpl --- .../org/gephi/graph/impl/ColumnIndexImpl.java | 725 +---------------- .../gephi/graph/impl/ColumnNoIndexImpl.java | 27 +- .../graph/impl/ColumnStandardIndexImpl.java | 750 ++++++++++++++++++ .../java/org/gephi/graph/impl/IndexImpl.java | 56 +- .../org/gephi/graph/impl/IndexImplTest.java | 4 +- 5 files changed, 812 insertions(+), 750 deletions(-) create mode 100644 src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java diff --git a/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java index 73cdfcd6..d01816db 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnIndexImpl.java @@ -15,731 +15,18 @@ */ package org.gephi.graph.impl; -import it.unimi.dsi.fastutil.booleans.BooleanArrays; -import it.unimi.dsi.fastutil.bytes.Byte2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.bytes.ByteArrays; -import it.unimi.dsi.fastutil.chars.Char2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.chars.CharArrays; -import it.unimi.dsi.fastutil.doubles.Double2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.doubles.DoubleArrays; -import it.unimi.dsi.fastutil.floats.Float2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.floats.FloatArrays; -import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.ints.IntArrays; -import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.longs.LongArrays; -import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrays; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import it.unimi.dsi.fastutil.shorts.Short2ObjectAVLTreeMap; -import it.unimi.dsi.fastutil.shorts.ShortArrays; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; import org.gephi.graph.api.ColumnIndex; import org.gephi.graph.api.Element; -public abstract class ColumnIndexImpl implements ColumnIndex { +public interface ColumnIndexImpl extends ColumnIndex { - // Const - public static final boolean TRIMMING_ENABLED = false; - public static final int TRIMMING_FREQUENCY = 30; - // Lock (optional) - protected final TableLockImpl lock; - // Data - protected final ColumnImpl column; - protected final ValueSet nullSet; - protected Map> map; - // Variable - protected int elements; + void destroy(); - protected ColumnIndexImpl(ColumnImpl column) { - this.column = column; - this.nullSet = new ValueSet<>(null); - if (column.table != null) { - lock = column.table.getLock(); - } else { - lock = null; - } - } + void clear(); - protected K putValue(T element, K value) { - if (value == null) { - if (nullSet.add(element)) { - elements++; - } - } else { - ValueSet set = getValueSet(value); - if (set == null) { - set = addValue(value); - } - value = set.value; + K putValue(T element, K value); - if (set.add(element)) { - elements++; - } - } - return value; - } + void removeValue(T element, K value); - protected void removeValue(T element, K value) { - if (value == null) { - if (nullSet.remove(element)) { - elements--; - } - } else { - ValueSet set = getValueSet(value); - if (set.remove(element)) { - elements--; - } - if (set.isEmpty()) { - removeValue(value); - } - } - } - - protected K replaceValue(T element, K oldValue, K newValue) { - removeValue(element, oldValue); - return putValue(element, newValue); - } - - protected int getCount(K value) { - lock(); - try { - if (value == null) { - return nullSet.size(); - } - ValueSet valueSet = getValueSet(value); - if (valueSet != null) { - return valueSet.size(); - } else { - return 0; - } - } finally { - unlock(); - } - } - - @Override - public int count(K value) { - return getCount(value); - } - - @Override - public Collection values() { - lock(); - try { - return new ArrayList<>(new WithNullDecorator()); - } finally { - unlock(); - } - } - - @Override - public int countValues() { - lock(); - try { - return (nullSet.isEmpty() ? 0 : 1) + map.size(); - } finally { - unlock(); - } - } - - @Override - public int countElements() { - lock(); - try { - return elements; - } finally { - unlock(); - } - } - - @Override - public Number getMinValue() { - if (isSortable()) { - if (map.isEmpty()) { - return null; - } else { - return (Number) ((SortedMap) map).firstKey(); - } - } else { - throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column - .getTypeClass().getSimpleName() + ")."); - } - } - - @Override - public Number getMaxValue() { - if (isSortable()) { - if (map.isEmpty()) { - return null; - } else { - return (Number) ((SortedMap) map).lastKey(); - } - } else { - throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column - .getTypeClass().getSimpleName() + ")."); - } - } - - protected void destroy() { - map = null; - nullSet.clear(); - elements = 0; - } - - protected void clear() { - map.clear(); - nullSet.clear(); - elements = 0; - } - - @Override - public Iterator>> iterator() { - return new EntryIterator(); - } - - @Override - public Iterable get(K value) { - lock(); - ValueSet valueSet = getValueSet(value); - if (valueSet == null) { - return ValueSet.EMPTY; - } - return new LockableIterable<>(valueSet.set); - } - - protected ValueSet getValueSet(K value) { - if (value == null) { - return nullSet; - } - return map.get(value); - } - - protected void removeValue(K value) { - map.remove(value); - } - - protected ValueSet addValue(K value) { - ValueSet valueSet = new ValueSet<>(value); - map.put(value, valueSet); - return valueSet; - } - - @Override - public boolean isSortable() { - return Number.class.isAssignableFrom(column.getTypeClass()) && map instanceof SortedMap; - } - - @Override - public ColumnImpl getColumn() { - return column; - } - - void lock() { - if (lock != null) { - lock.lock(); - } - } - - void unlock() { - if (lock != null) { - lock.unlock(); - } - } - - protected static class DefaultIndex extends ColumnIndexImpl { - - public DefaultIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenHashMap<>(); - } - } - - protected static class BooleanIndex extends ColumnIndexImpl { - - public BooleanIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenHashMap<>(); - } - } - - protected static class DoubleIndex extends ColumnIndexImpl { - - public DoubleIndex(ColumnImpl column) { - super(column); - - map = new Double2ObjectAVLTreeMap<>(); - } - } - - protected static class IntegerIndex extends ColumnIndexImpl { - - public IntegerIndex(ColumnImpl column) { - super(column); - - map = new Int2ObjectAVLTreeMap<>(); - } - } - - protected static class FloatIndex extends ColumnIndexImpl { - - public FloatIndex(ColumnImpl column) { - super(column); - - map = new Float2ObjectAVLTreeMap<>(); - } - } - - protected static class LongIndex extends ColumnIndexImpl { - - public LongIndex(ColumnImpl column) { - super(column); - - map = new Long2ObjectAVLTreeMap<>(); - } - } - - protected static class ShortIndex extends ColumnIndexImpl { - - public ShortIndex(ColumnImpl column) { - super(column); - - map = new Short2ObjectAVLTreeMap<>(); - } - } - - protected static class ByteIndex extends ColumnIndexImpl { - - public ByteIndex(ColumnImpl column) { - super(column); - - map = new Byte2ObjectAVLTreeMap<>(); - } - } - - protected static class GenericNumberIndex extends ColumnIndexImpl { - - public GenericNumberIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectAVLTreeMap<>(); - } - } - - protected static class CharIndex extends ColumnIndexImpl { - - public CharIndex(ColumnImpl column) { - super(column); - - map = new Char2ObjectAVLTreeMap<>(); - } - } - - protected static class DefaultArrayIndex extends ColumnIndexImpl { - - public DefaultArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(ObjectArrays.HASH_STRATEGY); - } - } - - protected static class BooleanArrayIndex extends ColumnIndexImpl { - - public BooleanArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(BooleanArrays.HASH_STRATEGY); - } - } - - protected static class DoubleArrayIndex extends ColumnIndexImpl { - - public DoubleArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(DoubleArrays.HASH_STRATEGY); - } - } - - protected static class IntegerArrayIndex extends ColumnIndexImpl { - - public IntegerArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(IntArrays.HASH_STRATEGY); - } - } - - protected static class FloatArrayIndex extends ColumnIndexImpl { - - public FloatArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(FloatArrays.HASH_STRATEGY); - } - } - - protected static class LongArrayIndex extends ColumnIndexImpl { - - public LongArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(LongArrays.HASH_STRATEGY); - } - } - - protected static class ShortArrayIndex extends ColumnIndexImpl { - - public ShortArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(ShortArrays.HASH_STRATEGY); - } - } - - protected static class ByteArrayIndex extends ColumnIndexImpl { - - public ByteArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(ByteArrays.HASH_STRATEGY); - } - } - - protected static class CharArrayIndex extends ColumnIndexImpl { - - public CharArrayIndex(ColumnImpl column) { - super(column); - - map = new Object2ObjectOpenCustomHashMap<>(CharArrays.HASH_STRATEGY); - } - } - - protected static final class ValueSet implements Set { - - protected static ValueSet EMPTY = new ValueSet(null); - protected final K value; - private final Set set; - - public ValueSet(K value) { - this.value = value; - this.set = new ObjectOpenHashSet<>(); - } - - @Override - public int size() { - return set.size(); - } - - @Override - public boolean isEmpty() { - return set.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return set.contains(o); - } - - @Override - public Iterator iterator() { - return set.iterator(); - } - - @Override - public Object[] toArray() { - return set.toArray(); - } - - @Override - public T[] toArray(T[] ts) { - return set.toArray(ts); - } - - @Override - public boolean add(T e) { - return set.add(e); - } - - @Override - public boolean remove(Object o) { - return set.remove(o); - } - - @Override - public boolean containsAll(Collection clctn) { - return set.containsAll(clctn); - } - - @Override - public boolean addAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported operation."); - } - - @Override - public boolean retainAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported operation."); - } - - @Override - public boolean removeAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported operation."); - } - - @Override - public void clear() { - set.clear(); - } - - @Override - public boolean equals(Object o) { - return set.equals(o); - } - - @Override - public int hashCode() { - return set.hashCode(); - } - } - - protected final class WithNullDecorator implements Collection { - - private boolean hasNull() { - return !nullSet.isEmpty(); - } - - @Override - public int size() { - return (hasNull() ? 1 : 0) + map.size(); - } - - @Override - public boolean isEmpty() { - return !hasNull() && map.isEmpty(); - } - - @Override - public boolean contains(Object o) { - if (o == null && hasNull()) { - return true; - } else if (o != null) { - return map.containsKey((K) o); - } - return false; - } - - @Override - public Iterator iterator() { - return new WithNullDecorator.WithNullIterator(); - } - - @Override - public Object[] toArray() { - if (hasNull()) { - Object[] res = new Object[map.size() + 1]; - res[0] = null; - System.arraycopy(map.keySet().toArray(), 0, res, 1, map.size()); - return res; - } else { - return map.keySet().toArray(); - } - } - - @Override - public V[] toArray(V[] array) { - - if (hasNull()) { - if (array.length < size()) { - array = (V[]) java.lang.reflect.Array - .newInstance(array.getClass().getComponentType(), map.size() + 1); - } - array[0] = null; - System.arraycopy(map.keySet().toArray(), 0, array, 1, map.size()); - return array; - } else { - return map.keySet().toArray(array); - } - } - - @Override - public boolean add(K e) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public boolean remove(Object o) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public boolean containsAll(Collection clctn) { - for (Object o : clctn) { - if (o == null && nullSet.isEmpty()) { - return false; - } else if (o != null && !map.containsKey((K) o)) { - return false; - } - } - return true; - } - - @Override - public boolean addAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public boolean removeAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public boolean retainAll(Collection clctn) { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public void clear() { - throw new UnsupportedOperationException("Not supported"); - } - - private final class WithNullIterator implements Iterator { - - private final Iterator mapIterator; - private boolean hasNull; - - public WithNullIterator() { - hasNull = hasNull(); - mapIterator = map.keySet().iterator(); - } - - @Override - public boolean hasNext() { - if (hasNull) { - return true; - } - return mapIterator.hasNext(); - } - - @Override - public K next() { - if (hasNull) { - hasNull = false; - return null; - } - return mapIterator.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Not supported operation."); - } - } - } - - private final class EntryIterator implements Iterator>> { - - private final Iterator>> mapIterator; - private NullEntry nullEntry; - - public EntryIterator() { - if (!nullSet.isEmpty()) { - nullEntry = new NullEntry(); - } - mapIterator = map.entrySet().iterator(); - } - - @Override - public boolean hasNext() { - if (nullEntry != null) { - return true; - } - return mapIterator.hasNext(); - } - - @Override - public Map.Entry> next() { - if (nullEntry != null) { - NullEntry ne = nullEntry; - nullEntry = null; - return ne; - } - return mapIterator.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Not supported operation."); - } - } - - private class NullEntry implements Map.Entry> { - - @Override - public K getKey() { - return null; - } - - @Override - public Set getValue() { - return nullSet; - } - - @Override - public Set setValue(Set v) { - throw new UnsupportedOperationException("Not supported operation."); - } - } - - private class LockableIterable implements Iterable { - - private final Iterable ite; - - public LockableIterable(Iterable ite) { - this.ite = ite; - } - - @Override - public Iterator iterator() { - return new LockableIterator<>(ite.iterator()); - } - } - - private class LockableIterator implements Iterator { - - private final Iterator itr; - - public LockableIterator(Iterator itr) { - this.itr = itr; - } - - @Override - public boolean hasNext() { - boolean n = itr.hasNext(); - if (!n && lock != null) { - lock.unlock(); - } - return n; - } - - @Override - public T next() { - return itr.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Not supported."); - } - } + K replaceValue(T element, K oldValue, K newValue); } diff --git a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java index 691cc580..78c2802d 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java @@ -27,7 +27,7 @@ import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; -public class ColumnNoIndexImpl implements ColumnIndex { +public class ColumnNoIndexImpl implements ColumnIndexImpl { // Data protected final ColumnImpl column; @@ -166,6 +166,31 @@ public Column getColumn() { throw new UnsupportedOperationException("Not implemented yet"); } + @Override + public void clear() { + // Nothing to clear + } + + @Override + public void destroy() { + // Nothing to destroy + } + + @Override + public K putValue(T element, K value) { + return value; + } + + @Override + public K replaceValue(T element, K oldValue, K newValue) { + return newValue; + } + + @Override + public void removeValue(T element, K value) { + // Nothing to remove + } + private class ElementWithValueIterable implements Iterable { private final Iterator ite; diff --git a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java new file mode 100644 index 00000000..8c822113 --- /dev/null +++ b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java @@ -0,0 +1,750 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; + +import it.unimi.dsi.fastutil.booleans.BooleanArrays; +import it.unimi.dsi.fastutil.bytes.Byte2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.bytes.ByteArrays; +import it.unimi.dsi.fastutil.chars.Char2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.chars.CharArrays; +import it.unimi.dsi.fastutil.doubles.Double2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.doubles.DoubleArrays; +import it.unimi.dsi.fastutil.floats.Float2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.floats.FloatArrays; +import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.ints.IntArrays; +import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.longs.LongArrays; +import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrays; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.shorts.Short2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.shorts.ShortArrays; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import org.gephi.graph.api.ColumnIndex; +import org.gephi.graph.api.Element; + +public abstract class ColumnStandardIndexImpl implements ColumnIndexImpl { + + // Const + public static final boolean TRIMMING_ENABLED = false; + public static final int TRIMMING_FREQUENCY = 30; + // Lock (optional) + protected final TableLockImpl lock; + // Data + protected final ColumnImpl column; + protected final ValueSet nullSet; + protected Map> map; + // Variable + protected int elements; + + protected ColumnStandardIndexImpl(ColumnImpl column) { + this.column = column; + this.nullSet = new ValueSet<>(null); + if (column.table != null) { + lock = column.table.getLock(); + } else { + lock = null; + } + } + + @Override + public K putValue(T element, K value) { + if (value == null) { + if (nullSet.add(element)) { + elements++; + } + } else { + ValueSet set = getValueSet(value); + if (set == null) { + set = addValue(value); + } + value = set.value; + + if (set.add(element)) { + elements++; + } + } + return value; + } + + @Override + public void removeValue(T element, K value) { + if (value == null) { + if (nullSet.remove(element)) { + elements--; + } + } else { + ValueSet set = getValueSet(value); + if (set.remove(element)) { + elements--; + } + if (set.isEmpty()) { + removeValue(value); + } + } + } + + @Override + public K replaceValue(T element, K oldValue, K newValue) { + removeValue(element, oldValue); + return putValue(element, newValue); + } + + protected int getCount(K value) { + lock(); + try { + if (value == null) { + return nullSet.size(); + } + ValueSet valueSet = getValueSet(value); + if (valueSet != null) { + return valueSet.size(); + } else { + return 0; + } + } finally { + unlock(); + } + } + + @Override + public int count(K value) { + return getCount(value); + } + + @Override + public Collection values() { + lock(); + try { + return new ArrayList<>(new WithNullDecorator()); + } finally { + unlock(); + } + } + + @Override + public int countValues() { + lock(); + try { + return (nullSet.isEmpty() ? 0 : 1) + map.size(); + } finally { + unlock(); + } + } + + @Override + public int countElements() { + lock(); + try { + return elements; + } finally { + unlock(); + } + } + + @Override + public Number getMinValue() { + if (isSortable()) { + if (map.isEmpty()) { + return null; + } else { + return (Number) ((SortedMap) map).firstKey(); + } + } else { + throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column + .getTypeClass().getSimpleName() + ")."); + } + } + + @Override + public Number getMaxValue() { + if (isSortable()) { + if (map.isEmpty()) { + return null; + } else { + return (Number) ((SortedMap) map).lastKey(); + } + } else { + throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column + .getTypeClass().getSimpleName() + ")."); + } + } + + @Override + public void destroy() { + map = null; + nullSet.clear(); + elements = 0; + } + + @Override + public void clear() { + map.clear(); + nullSet.clear(); + elements = 0; + } + + @Override + public Iterator>> iterator() { + return new EntryIterator(); + } + + @Override + public Iterable get(K value) { + lock(); + ValueSet valueSet = getValueSet(value); + if (valueSet == null) { + return ValueSet.EMPTY; + } + return new LockableIterable<>(valueSet.set); + } + + protected ValueSet getValueSet(K value) { + if (value == null) { + return nullSet; + } + return map.get(value); + } + + protected void removeValue(K value) { + map.remove(value); + } + + protected ValueSet addValue(K value) { + ValueSet valueSet = new ValueSet<>(value); + map.put(value, valueSet); + return valueSet; + } + + @Override + public boolean isSortable() { + return Number.class.isAssignableFrom(column.getTypeClass()) && map instanceof SortedMap; + } + + @Override + public ColumnImpl getColumn() { + return column; + } + + void lock() { + if (lock != null) { + lock.lock(); + } + } + + void unlock() { + if (lock != null) { + lock.unlock(); + } + } + + protected static class DefaultStandardIndex extends ColumnStandardIndexImpl { + + public DefaultStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenHashMap<>(); + } + } + + protected static class BooleanStandardIndex extends ColumnStandardIndexImpl { + + public BooleanStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenHashMap<>(); + } + } + + protected static class DoubleStandardIndex extends ColumnStandardIndexImpl { + + public DoubleStandardIndex(ColumnImpl column) { + super(column); + + map = new Double2ObjectAVLTreeMap<>(); + } + } + + protected static class IntegerStandardIndex extends ColumnStandardIndexImpl { + + public IntegerStandardIndex(ColumnImpl column) { + super(column); + + map = new Int2ObjectAVLTreeMap<>(); + } + } + + protected static class FloatStandardIndex extends ColumnStandardIndexImpl { + + public FloatStandardIndex(ColumnImpl column) { + super(column); + + map = new Float2ObjectAVLTreeMap<>(); + } + } + + protected static class LongStandardIndex extends ColumnStandardIndexImpl { + + public LongStandardIndex(ColumnImpl column) { + super(column); + + map = new Long2ObjectAVLTreeMap<>(); + } + } + + protected static class ShortStandardIndex extends ColumnStandardIndexImpl { + + public ShortStandardIndex(ColumnImpl column) { + super(column); + + map = new Short2ObjectAVLTreeMap<>(); + } + } + + protected static class ByteStandardIndex extends ColumnStandardIndexImpl { + + public ByteStandardIndex(ColumnImpl column) { + super(column); + + map = new Byte2ObjectAVLTreeMap<>(); + } + } + + protected static class GenericNumberStandardIndex extends ColumnStandardIndexImpl { + + public GenericNumberStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectAVLTreeMap<>(); + } + } + + protected static class CharStandardIndex extends ColumnStandardIndexImpl { + + public CharStandardIndex(ColumnImpl column) { + super(column); + + map = new Char2ObjectAVLTreeMap<>(); + } + } + + protected static class DefaultArrayStandardIndex extends ColumnStandardIndexImpl { + + public DefaultArrayStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(ObjectArrays.HASH_STRATEGY); + } + } + + protected static class BooleanArrayStandardIndex extends ColumnStandardIndexImpl { + + public BooleanArrayStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(BooleanArrays.HASH_STRATEGY); + } + } + + protected static class DoubleArrayStandardIndex extends ColumnStandardIndexImpl { + + public DoubleArrayStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(DoubleArrays.HASH_STRATEGY); + } + } + + protected static class IntegerArrayStandardIndex extends ColumnStandardIndexImpl { + + public IntegerArrayStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(IntArrays.HASH_STRATEGY); + } + } + + protected static class FloatArrayStandardIndex extends ColumnStandardIndexImpl { + + public FloatArrayStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(FloatArrays.HASH_STRATEGY); + } + } + + protected static class LongArrayStandardIndex extends ColumnStandardIndexImpl { + + public LongArrayStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(LongArrays.HASH_STRATEGY); + } + } + + protected static class ShortArrayStandardIndex extends ColumnStandardIndexImpl { + + public ShortArrayStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(ShortArrays.HASH_STRATEGY); + } + } + + protected static class ByteArrayStandardIndex extends ColumnStandardIndexImpl { + + public ByteArrayStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(ByteArrays.HASH_STRATEGY); + } + } + + protected static class CharArrayStandardIndex extends ColumnStandardIndexImpl { + + public CharArrayStandardIndex(ColumnImpl column) { + super(column); + + map = new Object2ObjectOpenCustomHashMap<>(CharArrays.HASH_STRATEGY); + } + } + + protected static final class ValueSet implements Set { + + protected static ValueSet EMPTY = new ValueSet(null); + protected final K value; + private final Set set; + + public ValueSet(K value) { + this.value = value; + this.set = new ObjectOpenHashSet<>(); + } + + @Override + public int size() { + return set.size(); + } + + @Override + public boolean isEmpty() { + return set.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return set.contains(o); + } + + @Override + public Iterator iterator() { + return set.iterator(); + } + + @Override + public Object[] toArray() { + return set.toArray(); + } + + @Override + public T[] toArray(T[] ts) { + return set.toArray(ts); + } + + @Override + public boolean add(T e) { + return set.add(e); + } + + @Override + public boolean remove(Object o) { + return set.remove(o); + } + + @Override + public boolean containsAll(Collection clctn) { + return set.containsAll(clctn); + } + + @Override + public boolean addAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported operation."); + } + + @Override + public boolean retainAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported operation."); + } + + @Override + public boolean removeAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported operation."); + } + + @Override + public void clear() { + set.clear(); + } + + @Override + public boolean equals(Object o) { + return set.equals(o); + } + + @Override + public int hashCode() { + return set.hashCode(); + } + } + + protected final class WithNullDecorator implements Collection { + + private boolean hasNull() { + return !nullSet.isEmpty(); + } + + @Override + public int size() { + return (hasNull() ? 1 : 0) + map.size(); + } + + @Override + public boolean isEmpty() { + return !hasNull() && map.isEmpty(); + } + + @Override + public boolean contains(Object o) { + if (o == null && hasNull()) { + return true; + } else if (o != null) { + return map.containsKey((K) o); + } + return false; + } + + @Override + public Iterator iterator() { + return new WithNullDecorator.WithNullIterator(); + } + + @Override + public Object[] toArray() { + if (hasNull()) { + Object[] res = new Object[map.size() + 1]; + res[0] = null; + System.arraycopy(map.keySet().toArray(), 0, res, 1, map.size()); + return res; + } else { + return map.keySet().toArray(); + } + } + + @Override + public V[] toArray(V[] array) { + + if (hasNull()) { + if (array.length < size()) { + array = (V[]) java.lang.reflect.Array + .newInstance(array.getClass().getComponentType(), map.size() + 1); + } + array[0] = null; + System.arraycopy(map.keySet().toArray(), 0, array, 1, map.size()); + return array; + } else { + return map.keySet().toArray(array); + } + } + + @Override + public boolean add(K e) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean containsAll(Collection clctn) { + for (Object o : clctn) { + if (o == null && nullSet.isEmpty()) { + return false; + } else if (o != null && !map.containsKey((K) o)) { + return false; + } + } + return true; + } + + @Override + public boolean addAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean removeAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean retainAll(Collection clctn) { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public void clear() { + throw new UnsupportedOperationException("Not supported"); + } + + private final class WithNullIterator implements Iterator { + + private final Iterator mapIterator; + private boolean hasNull; + + public WithNullIterator() { + hasNull = hasNull(); + mapIterator = map.keySet().iterator(); + } + + @Override + public boolean hasNext() { + if (hasNull) { + return true; + } + return mapIterator.hasNext(); + } + + @Override + public K next() { + if (hasNull) { + hasNull = false; + return null; + } + return mapIterator.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported operation."); + } + } + } + + private final class EntryIterator implements Iterator>> { + + private final Iterator>> mapIterator; + private NullEntry nullEntry; + + public EntryIterator() { + if (!nullSet.isEmpty()) { + nullEntry = new NullEntry(); + } + mapIterator = map.entrySet().iterator(); + } + + @Override + public boolean hasNext() { + if (nullEntry != null) { + return true; + } + return mapIterator.hasNext(); + } + + @Override + public Map.Entry> next() { + if (nullEntry != null) { + NullEntry ne = nullEntry; + nullEntry = null; + return ne; + } + return mapIterator.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported operation."); + } + } + + private class NullEntry implements Map.Entry> { + + @Override + public K getKey() { + return null; + } + + @Override + public Set getValue() { + return nullSet; + } + + @Override + public Set setValue(Set v) { + throw new UnsupportedOperationException("Not supported operation."); + } + } + + private class LockableIterable implements Iterable { + + private final Iterable ite; + + public LockableIterable(Iterable ite) { + this.ite = ite; + } + + @Override + public Iterator iterator() { + return new LockableIterator<>(ite.iterator()); + } + } + + private class LockableIterator implements Iterator { + + private final Iterator itr; + + public LockableIterator(Iterator itr) { + this.itr = itr; + } + + @Override + public boolean hasNext() { + boolean n = itr.hasNext(); + if (!n && lock != null) { + lock.unlock(); + } + return n; + } + + @Override + public T next() { + return itr.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } + } +} diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index 5774dfe1..625009a1 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -58,7 +58,7 @@ public int count(Column column, Object value) { lock(); try { - return getIndex(column).getCount(value); + return getIndex(column).count(value); } finally { unlock(); } @@ -157,7 +157,7 @@ public int countElements(Column column) { checkNonNullColumnObject(column); lock(); try { - return getIndex(column).elements; + return getIndex(column).countElements(); } finally { unlock(); } @@ -210,7 +210,7 @@ public void clear() { protected void addColumn(ColumnImpl col) { if (col.isIndexed()) { ensureColumnSize(col.storeId); - ColumnIndexImpl index = createIndex(col); + ColumnStandardIndexImpl index = createStandardIndex(col); columns[col.storeId] = index; columnsCount++; } @@ -220,7 +220,7 @@ protected void addAllColumns(ColumnImpl[] cols) { ensureColumnSize(cols.length); for (ColumnImpl col : cols) { if (col.isIndexed()) { - ColumnIndexImpl index = createIndex(col); + ColumnStandardIndexImpl index = createStandardIndex(col); columns[col.storeId] = index; columnsCount++; } @@ -239,7 +239,7 @@ protected void removeColumn(ColumnImpl col) { protected boolean hasColumn(ColumnImpl col) { if (col.isIndexed()) { int id = col.storeId; - if (id != ColumnStore.NULL_ID && columns.length > id && columns[id].column == col) { + if (id != ColumnStore.NULL_ID && columns.length > id && columns[id].getColumn() == col) { return true; } } @@ -251,7 +251,7 @@ protected ColumnIndexImpl getIndex(Column col) { int id = col.getIndex(); if (id != ColumnStore.NULL_ID && columns.length > id) { ColumnIndexImpl index = columns[id]; - if (index != null && index.column == col) { + if (index != null && index.getColumn() == col) { return index; } } @@ -277,69 +277,69 @@ protected int size() { return columnsCount; } - ColumnIndexImpl createIndex(ColumnImpl column) { + ColumnStandardIndexImpl createStandardIndex(ColumnImpl column) { if (column.getTypeClass().equals(Byte.class)) { // Byte - return new ColumnIndexImpl.ByteIndex(column); + return new ColumnStandardIndexImpl.ByteStandardIndex(column); } else if (column.getTypeClass().equals(Short.class)) { // Short - return new ColumnIndexImpl.ShortIndex(column); + return new ColumnStandardIndexImpl.ShortStandardIndex(column); } else if (column.getTypeClass().equals(Integer.class)) { // Integer - return new ColumnIndexImpl.IntegerIndex(column); + return new ColumnStandardIndexImpl.IntegerStandardIndex(column); } else if (column.getTypeClass().equals(Long.class)) { // Long - return new ColumnIndexImpl.LongIndex(column); + return new ColumnStandardIndexImpl.LongStandardIndex(column); } else if (column.getTypeClass().equals(Float.class)) { // Float - return new ColumnIndexImpl.FloatIndex(column); + return new ColumnStandardIndexImpl.FloatStandardIndex(column); } else if (column.getTypeClass().equals(Double.class)) { // Double - return new ColumnIndexImpl.DoubleIndex(column); + return new ColumnStandardIndexImpl.DoubleStandardIndex(column); } else if (Number.class.isAssignableFrom(column.getTypeClass())) { // Other numbers - return new ColumnIndexImpl.GenericNumberIndex(column); + return new ColumnStandardIndexImpl.GenericNumberStandardIndex(column); } else if (column.getTypeClass().equals(Boolean.class)) { // Boolean - return new ColumnIndexImpl.BooleanIndex(column); + return new ColumnStandardIndexImpl.BooleanStandardIndex(column); } else if (column.getTypeClass().equals(Character.class)) { // Char - return new ColumnIndexImpl.CharIndex(column); + return new ColumnStandardIndexImpl.CharStandardIndex(column); } else if (column.getTypeClass().equals(String.class)) { // String - return new ColumnIndexImpl.DefaultIndex(column); + return new ColumnStandardIndexImpl.DefaultStandardIndex(column); } else if (column.getTypeClass().equals(byte[].class)) { // Byte Array - return new ColumnIndexImpl.ByteArrayIndex(column); + return new ColumnStandardIndexImpl.ByteArrayStandardIndex(column); } else if (column.getTypeClass().equals(short[].class)) { // Short Array - return new ColumnIndexImpl.ShortArrayIndex(column); + return new ColumnStandardIndexImpl.ShortArrayStandardIndex(column); } else if (column.getTypeClass().equals(int[].class)) { // Integer Array - return new ColumnIndexImpl.IntegerArrayIndex(column); + return new ColumnStandardIndexImpl.IntegerArrayStandardIndex(column); } else if (column.getTypeClass().equals(long[].class)) { // Long Array - return new ColumnIndexImpl.LongArrayIndex(column); + return new ColumnStandardIndexImpl.LongArrayStandardIndex(column); } else if (column.getTypeClass().equals(float[].class)) { // Float array - return new ColumnIndexImpl.FloatArrayIndex(column); + return new ColumnStandardIndexImpl.FloatArrayStandardIndex(column); } else if (column.getTypeClass().equals(double[].class)) { // Double array - return new ColumnIndexImpl.DoubleArrayIndex(column); + return new ColumnStandardIndexImpl.DoubleArrayStandardIndex(column); } else if (column.getTypeClass().equals(boolean[].class)) { // Boolean array - return new ColumnIndexImpl.BooleanArrayIndex(column); + return new ColumnStandardIndexImpl.BooleanArrayStandardIndex(column); } else if (column.getTypeClass().equals(char[].class)) { // Char array - return new ColumnIndexImpl.CharArrayIndex(column); + return new ColumnStandardIndexImpl.CharArrayStandardIndex(column); } else if (column.getTypeClass().equals(String[].class)) { // String array - return new ColumnIndexImpl.DefaultArrayIndex(column); + return new ColumnStandardIndexImpl.DefaultArrayStandardIndex(column); } else if (column.getTypeClass().isArray()) { // Default Array - return new ColumnIndexImpl.DefaultArrayIndex(column); + return new ColumnStandardIndexImpl.DefaultArrayStandardIndex(column); } - return new ColumnIndexImpl.DefaultIndex(column); + return new ColumnStandardIndexImpl.DefaultStandardIndex(column); } private void ensureColumnSize(int index) { diff --git a/src/test/java/org/gephi/graph/impl/IndexImplTest.java b/src/test/java/org/gephi/graph/impl/IndexImplTest.java index 80f8b689..9337bc5e 100644 --- a/src/test/java/org/gephi/graph/impl/IndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexImplTest.java @@ -314,7 +314,7 @@ public void testWithNullDecorator() { index.put(fooColumn, null, n1); index.put(fooColumn, "bar", n3); - ColumnIndexImpl withNullIndex = index.getIndex("foo"); + ColumnStandardIndexImpl withNullIndex = index.getIndex("foo"); Collection withNullCollection = withNullIndex.values(); Assert.assertEquals(withNullCollection.size(), 2); Assert.assertFalse(withNullCollection.isEmpty()); @@ -332,7 +332,7 @@ public void testWithNullDecorator() { Assert.assertEquals(withNullItr.next(), "bar"); Assert.assertFalse(withNullItr.hasNext()); - ColumnIndexImpl withoutNullIndex = index.getIndex("age"); + ColumnStandardIndexImpl withoutNullIndex = index.getIndex("age"); Collection withoutNullCollection = withoutNullIndex.values(); Assert.assertEquals(withoutNullCollection.size(), 2); Assert.assertFalse(withoutNullCollection.isEmpty()); From 6aff889abce4221f72677eccdd1e626b0d834724 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 27 Dec 2021 09:55:28 +0100 Subject: [PATCH 045/271] Cleanup --- .../java/org/gephi/graph/impl/ElementImpl.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index b378a6ab..4a41a02c 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -15,6 +15,7 @@ */ package org.gephi.graph.impl; +import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Map; import java.util.Set; @@ -160,7 +161,7 @@ public Object getAttribute(Column column, GraphView view) { return getAttribute(column); } else { Interval interval = view.getTimeInterval(); - checkViewExist((GraphView) view); + checkViewExist(view); int index = column.getIndex(); synchronized (this) { @@ -407,11 +408,13 @@ private void setTimeAttribute(Column column, Object value, Object timeObject) { oldValue = attributes[index]; } - TimeMap dynamicValue = null; + TimeMap dynamicValue; if (oldValue == null) { try { - attributes[index] = dynamicValue = (TimeMap) column.getTypeClass().newInstance(); - } catch (InstantiationException | IllegalAccessException ex) { + attributes[index] = dynamicValue = (TimeMap) column.getTypeClass().getDeclaredConstructor() + .newInstance(); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException + | InvocationTargetException ex) { throw new RuntimeException(ex); } } else { @@ -669,10 +672,7 @@ public boolean equals(Object obj) { return false; } final ElementImpl other = (ElementImpl) obj; - if (!this.getId().equals(other.getId())) { - return false; - } - return true; + return this.getId().equals(other.getId()); } protected GraphStore getGraphStore() { From 6988ca25a6d599752764d0d746bfc550edf4a1d9 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 27 Dec 2021 09:56:32 +0100 Subject: [PATCH 046/271] Finish implementing alternative iteration based index for non-indexed columns #141 --- src/main/java/org/gephi/graph/api/Index.java | 2 +- .../gephi/graph/impl/ColumnNoIndexImpl.java | 2 +- .../graph/impl/ColumnStandardIndexImpl.java | 5 +- .../graph/impl/GraphStoreConfiguration.java | 8 + .../java/org/gephi/graph/impl/IndexImpl.java | 65 +- .../java/org/gephi/graph/impl/IndexStore.java | 2 +- .../graph/impl/ColumnStandardIndexTest.java | 638 +++++++++++++++++ .../org/gephi/graph/impl/IndexImplTest.java | 641 +----------------- .../org/gephi/graph/impl/IndexStoreTest.java | 2 +- 9 files changed, 702 insertions(+), 663 deletions(-) create mode 100644 src/test/java/org/gephi/graph/impl/ColumnStandardIndexTest.java diff --git a/src/main/java/org/gephi/graph/api/Index.java b/src/main/java/org/gephi/graph/api/Index.java index 0e563548..0b31dbbd 100644 --- a/src/main/java/org/gephi/graph/api/Index.java +++ b/src/main/java/org/gephi/graph/api/Index.java @@ -19,7 +19,7 @@ /** * An index is associated with each table and keeps track of each unique value - * in indexed columns. + * in columns. *

* Each column is associated with a @{{@link ColumnIndex}}. * diff --git a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java index 78c2802d..263eb2ab 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; @@ -21,7 +22,6 @@ import java.util.Map; import java.util.Set; import org.gephi.graph.api.Column; -import org.gephi.graph.api.ColumnIndex; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; import org.gephi.graph.api.Graph; diff --git a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java index 8c822113..8255526d 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; import it.unimi.dsi.fastutil.booleans.BooleanArrays; @@ -41,14 +42,10 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; -import org.gephi.graph.api.ColumnIndex; import org.gephi.graph.api.Element; public abstract class ColumnStandardIndexImpl implements ColumnIndexImpl { - // Const - public static final boolean TRIMMING_ENABLED = false; - public static final int TRIMMING_FREQUENCY = 30; // Lock (optional) protected final TableLockImpl lock; // Data diff --git a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java index 6172d8c5..5eccc28f 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java +++ b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java @@ -83,8 +83,16 @@ public final class GraphStoreConfiguration { // Miscellaneous public static final double TIMESTAMP_STORE_GROWING_FACTOR = 1.1; public static final double INTERVAL_STORE_GROWING_FACTOR = 1.1; + + /** + * Default number of node property columns. + */ public static final int NODE_DEFAULT_COLUMNS = 1 + (ENABLE_ELEMENT_LABEL ? 1 : 0) + (ENABLE_ELEMENT_TIME_SET ? 1 : 0); + + /** + * Default number of edge property columns. + */ public static final int EDGE_DEFAULT_COLUMNS = 2 + (ENABLE_ELEMENT_LABEL ? 1 : 0) + (ENABLE_ELEMENT_TIME_SET ? 1 : 0); } diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index 625009a1..c6c42b82 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -22,17 +22,25 @@ import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnIndex; import org.gephi.graph.api.Element; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Index; public class IndexImpl implements Index { protected final TableLockImpl lock; protected final ColumnStore columnStore; + protected final Graph graph; protected ColumnIndexImpl[] columns; protected int columnsCount; public IndexImpl(ColumnStore columnStore) { + this(columnStore, columnStore.graphStore); + } + + public IndexImpl(ColumnStore columnStore, Graph graph) { this.columnStore = columnStore; + this.graph = graph; this.columns = new ColumnIndexImpl[0]; this.lock = columnStore.lock; } @@ -208,52 +216,39 @@ public void clear() { } protected void addColumn(ColumnImpl col) { - if (col.isIndexed()) { - ensureColumnSize(col.storeId); - ColumnStandardIndexImpl index = createStandardIndex(col); - columns[col.storeId] = index; - columnsCount++; - } + ensureColumnSize(col.storeId); + ColumnIndexImpl index = createIndex(col); + columns[col.storeId] = index; + columnsCount++; } protected void addAllColumns(ColumnImpl[] cols) { ensureColumnSize(cols.length); for (ColumnImpl col : cols) { - if (col.isIndexed()) { - ColumnStandardIndexImpl index = createStandardIndex(col); - columns[col.storeId] = index; - columnsCount++; - } + ColumnIndexImpl index = createIndex(col); + columns[col.storeId] = index; + columnsCount++; } } protected void removeColumn(ColumnImpl col) { - if (col.isIndexed()) { - ColumnIndexImpl index = columns[col.storeId]; - index.destroy(); - columns[col.storeId] = null; - columnsCount--; - } + ColumnIndexImpl index = columns[col.storeId]; + index.destroy(); + columns[col.storeId] = null; + columnsCount--; } protected boolean hasColumn(ColumnImpl col) { - if (col.isIndexed()) { - int id = col.storeId; - if (id != ColumnStore.NULL_ID && columns.length > id && columns[id].getColumn() == col) { - return true; - } - } - return false; + int id = col.storeId; + return id != ColumnStore.NULL_ID && columns.length > id && columns[id].getColumn() == col; } protected ColumnIndexImpl getIndex(Column col) { - if (col.isIndexed()) { - int id = col.getIndex(); - if (id != ColumnStore.NULL_ID && columns.length > id) { - ColumnIndexImpl index = columns[id]; - if (index != null && index.getColumn() == col) { - return index; - } + int id = col.getIndex(); + if (id != ColumnStore.NULL_ID && columns.length > id) { + ColumnIndexImpl index = columns[id]; + if (index != null && index.getColumn() == col) { + return index; } } return null; @@ -277,6 +272,14 @@ protected int size() { return columnsCount; } + ColumnIndexImpl createIndex(ColumnImpl col) { + return col.isIndexed() ? createStandardIndex(col) : createNoIndex(col, graph); + } + + ColumnNoIndexImpl createNoIndex(ColumnImpl column, Graph graph) { + return new ColumnNoIndexImpl(column, graph, columnStore.elementType); + } + ColumnStandardIndexImpl createStandardIndex(ColumnImpl column) { if (column.getTypeClass().equals(Byte.class)) { // Byte diff --git a/src/main/java/org/gephi/graph/impl/IndexStore.java b/src/main/java/org/gephi/graph/impl/IndexStore.java index 211b0a10..818b3a2a 100644 --- a/src/main/java/org/gephi/graph/impl/IndexStore.java +++ b/src/main/java/org/gephi/graph/impl/IndexStore.java @@ -80,7 +80,7 @@ protected IndexImpl createViewIndex(Graph graph) { if (graph.getView().isMainView()) { throw new IllegalArgumentException("Can't create a view index for the main view"); } - IndexImpl viewIndex = new IndexImpl<>(columnStore); + IndexImpl viewIndex = new IndexImpl<>(columnStore, graph); ColumnImpl[] columns = columnStore.toArray(); viewIndex.addAllColumns(columns); viewIndexes.put(graph.getView(), viewIndex); diff --git a/src/test/java/org/gephi/graph/impl/ColumnStandardIndexTest.java b/src/test/java/org/gephi/graph/impl/ColumnStandardIndexTest.java new file mode 100644 index 00000000..be430498 --- /dev/null +++ b/src/test/java/org/gephi/graph/impl/ColumnStandardIndexTest.java @@ -0,0 +1,638 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; + +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import org.gephi.graph.api.Column; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.Origin; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class ColumnStandardIndexTest { + + @Test + public void testCount() { + IndexImpl index = generateEmptyIndex(); + NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); + putAll(nodes, index); + + for (Column col : index.columnStore) { + if (col.isIndexed()) { + Object2IntMap counter = new Object2IntOpenHashMap(); + for (NodeImpl n : nodes) { + Object obj = n.getAttribute(col); + counter.put(obj, counter.getInt(obj) + 1); + } + + for (Object2IntMap.Entry entry : counter.object2IntEntrySet()) { + int count = entry.getIntValue(); + Object val = entry.getKey(); + + Assert.assertEquals(index.count(col, val), count); + Assert.assertEquals(index.count(col.getId(), val), count); + } + } + } + } + + @Test + public void testGet() { + IndexImpl index = generateEmptyIndex(); + NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); + putAll(nodes, index); + + for (Column col : index.columnStore) { + if (col.isIndexed()) { + ObjectSet set = new ObjectOpenHashSet<>(); + for (NodeImpl n : nodes) { + Object obj = n.getAttribute(col); + set.add(obj); + } + + for (Object value : set) { + ObjectSet actual1 = new ObjectOpenHashSet(getIterable(index.get(col, value))); + ObjectSet actual2 = new ObjectOpenHashSet(getIterable(index.get(col.getId(), value))); + ObjectSet expected = new ObjectOpenHashSet(); + for (NodeImpl n : nodes) { + Object v = n.getAttribute(col); + if ((v == null && value == null) || (v != null && v.equals(value))) { + expected.add(n); + } + } + Assert.assertEquals(actual1, expected); + Assert.assertEquals(actual2, expected); + } + + for (Map.Entry> entry : index.get(col)) { + Object value = entry.getKey(); + Set actual = entry.getValue(); + + ObjectSet expected = new ObjectOpenHashSet(); + for (NodeImpl n : nodes) { + Object v = n.getAttribute(col); + if ((v == null && value == null) || (v != null && v.equals(value))) { + expected.add(n); + } + } + + Assert.assertEquals(actual, expected); + } + } + } + } + + @Test + public void testIsSortable() { + IndexImpl index = generateEmptyIndex(); + + Column ageCol = index.columnStore.getColumn("age"); + Column bigIntCol = index.columnStore.getColumn("big_int"); + Column fooCol = index.columnStore.getColumn("foo"); + + Assert.assertTrue(index.isSortable(ageCol)); + Assert.assertTrue(index.isSortable(bigIntCol)); + Assert.assertFalse(index.isSortable(fooCol)); + } + + @Test + public void testMinMaxValue() { + IndexImpl index = generateEmptyIndex(); + NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); + + Column ageCol = index.columnStore.getColumn("age"); + + Assert.assertNull(index.getMinValue(ageCol)); + Assert.assertNull(index.getMaxValue(ageCol)); + + putAll(nodes, index); + + int min = Integer.MAX_VALUE; + int max = Integer.MIN_VALUE; + for (NodeImpl n : nodes) { + Integer v = (Integer) n.getAttribute(ageCol); + if (v != null) { + min = Math.min(min, v); + max = Math.max(max, v); + } + } + + Assert.assertEquals(index.getMinValue(ageCol), min); + Assert.assertEquals(index.getMaxValue(ageCol), max); + } + + @Test + public void testMinMaxValueBigInteger() { + IndexImpl index = generateEmptyIndex(); + NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); + + Column bigIntCol = index.columnStore.getColumn("big_int"); + + Assert.assertNull(index.getMinValue(bigIntCol)); + Assert.assertNull(index.getMaxValue(bigIntCol)); + + putAll(nodes, index); + + BigInteger min = BigInteger.valueOf(Long.MAX_VALUE); + BigInteger max = BigInteger.valueOf(Long.MIN_VALUE); + for (NodeImpl n : nodes) { + BigInteger v = (BigInteger) n.getAttribute(bigIntCol); + if (v != null) { + if (v.compareTo(min) == -1) { + min = v; + } + if (v.compareTo(max) == 1) { + max = v; + } + } + } + + Assert.assertNotNull(index.getMinValue(bigIntCol)); + Assert.assertNotNull(index.getMaxValue(bigIntCol)); + + Assert.assertEquals(index.getMinValue(bigIntCol), min); + Assert.assertEquals(index.getMaxValue(bigIntCol), max); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testMinValueNoNumber() { + IndexImpl index = generateEmptyIndex(); + index.getMinValue(index.columnStore.getColumn("foo")); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testMaxValueNoNumber() { + IndexImpl index = generateEmptyIndex(); + index.getMaxValue(index.columnStore.getColumn("foo")); + } + + @Test + public void testValues() { + IndexImpl index = generateEmptyIndex(); + NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); + putAll(nodes, index); + + for (Column col : index.columnStore) { + if (col.isIndexed()) { + Collection collection = index.values(col); + + ObjectSet expected = new ObjectOpenHashSet<>(); + for (NodeImpl n : nodes) { + Object obj = n.getAttribute(col); + expected.add(obj); + } + + Assert.assertEquals(collection.size(), expected.size()); + Assert.assertEquals(index.countValues(col), expected.size()); + Assert.assertEquals(new ObjectOpenHashSet<>(collection), expected); + } + } + } + + @Test + public void testWithNullDecorator() { + IndexImpl index = generateEmptyIndex(); + Column ageColumn = index.columnStore.getColumn("age"); + Column fooColumn = index.columnStore.getColumn("foo"); + NodeImpl n1 = new NodeImpl(0); + NodeImpl n2 = new NodeImpl(1); + NodeImpl n3 = new NodeImpl(2); + index.put(ageColumn, 10, n1); + index.put(ageColumn, 20, n2); + index.put(fooColumn, null, n1); + index.put(fooColumn, "bar", n3); + + ColumnIndexImpl withNullIndex = index.getIndex("foo"); + Collection withNullCollection = withNullIndex.values(); + Assert.assertEquals(withNullCollection.size(), 2); + Assert.assertFalse(withNullCollection.isEmpty()); + Assert.assertTrue(withNullCollection.contains(null)); + Assert.assertTrue(withNullCollection.contains("bar")); + Assert.assertFalse(withNullCollection.contains("none")); + Assert.assertEquals(withNullCollection.toArray(), new Object[] { null, "bar" }); + Assert.assertEquals(withNullCollection.toArray(new Object[0]), new Object[] { null, "bar" }); + Assert.assertTrue(withNullCollection.containsAll(Arrays.asList(new Object[] { null, "bar" }))); + Assert.assertFalse(withNullCollection.containsAll(Arrays.asList(new Object[] { null, "none" }))); + Iterator withNullItr = withNullCollection.iterator(); + Assert.assertTrue(withNullItr.hasNext()); + Assert.assertNull(withNullItr.next()); + Assert.assertTrue(withNullItr.hasNext()); + Assert.assertEquals(withNullItr.next(), "bar"); + Assert.assertFalse(withNullItr.hasNext()); + + ColumnIndexImpl withoutNullIndex = index.getIndex("age"); + Collection withoutNullCollection = withoutNullIndex.values(); + Assert.assertEquals(withoutNullCollection.size(), 2); + Assert.assertFalse(withoutNullCollection.isEmpty()); + Assert.assertFalse(withoutNullCollection.contains(null)); + Assert.assertTrue(withoutNullCollection.contains(10)); + Assert.assertFalse(withoutNullCollection.contains(30)); + Assert.assertEquals(withoutNullCollection.toArray(), new Object[] { 10, 20 }); + Assert.assertEquals(withoutNullCollection.toArray(new Object[0]), new Object[] { 10, 20 }); + Assert.assertTrue(withoutNullCollection.containsAll(Arrays.asList(new Object[] { 10, 20 }))); + Assert.assertFalse(withoutNullCollection.containsAll(Arrays.asList(new Object[] { null }))); + Assert.assertFalse(withoutNullCollection.containsAll(Arrays.asList(new Object[] { 30 }))); + Iterator withoutNullItr = withoutNullCollection.iterator(); + Assert.assertTrue(withoutNullItr.hasNext()); + Assert.assertEquals(withoutNullItr.next(), 10); + Assert.assertTrue(withoutNullItr.hasNext()); + Assert.assertEquals(withoutNullItr.next(), 20); + Assert.assertFalse(withoutNullItr.hasNext()); + } + + @Test + public void testCountElements() { + IndexImpl index = generateEmptyIndex(); + + for (Column col : index.columnStore) { + if (col.isIndexed()) { + Assert.assertEquals(index.countElements(col), 0); + } + } + + NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); + putAll(nodes, index); + + for (Column col : index.columnStore) { + if (col.isIndexed()) { + Assert.assertEquals(index.countElements(col), nodes.length); + } + } + } + + @Test + public void testPut() { + IndexImpl index = generateEmptyIndex(); + Column column = index.columnStore.getColumn("age"); + NodeImpl n = new NodeImpl(0); + + Integer v = 10; + Assert.assertSame(index.put(column, v, n), v); + Assert.assertEquals(index.count(column, v), 1); + + Assert.assertSame(index.put(column, v, n), v); + Assert.assertEquals(index.count(column, v), 1); + } + + @Test + public void testPutManagedValue() { + IndexImpl index = generateEmptyIndex(); + Column column = index.columnStore.getColumn("age"); + NodeImpl n1 = new NodeImpl(0); + NodeImpl n2 = new NodeImpl(1); + + Integer v = 10; + index.put(column, v, n1); + Assert.assertSame(index.put(column, 10, n2), v); + } + + @Test + public void testRemoveByColumn() { + IndexImpl index = generateEmptyIndex(); + Column column = index.columnStore.getColumn("age"); + NodeImpl n = new NodeImpl(0); + index.put(column, 10, n); + index.remove(column, 10, n); + + Assert.assertEquals(index.count(column, 10), 0); + } + + @Test + public void testRemoveByString() { + IndexImpl index = generateEmptyIndex(); + Column column = index.columnStore.getColumn("age"); + NodeImpl n = new NodeImpl(0); + index.put(column, 10, n); + index.remove("age", 10, n); + + Assert.assertEquals(index.count(column, 10), 0); + } + + @Test + public void testSetByColumn() { + IndexImpl index = generateEmptyIndex(); + Column column = index.columnStore.getColumn("age"); + NodeImpl n = new NodeImpl(0); + index.put(column, 10, n); + index.set(column, 10, 20, n); + + Assert.assertEquals(index.count(column, 10), 0); + Assert.assertEquals(index.count(column, 20), 1); + Assert.assertEquals(index.countValues(column), 1); + Assert.assertEquals(index.countElements(column), 1); + } + + @Test + public void testSetByString() { + IndexImpl index = generateEmptyIndex(); + Column column = index.columnStore.getColumn("age"); + NodeImpl n = new NodeImpl(0); + index.put(column, 10, n); + index.set("age", 10, 20, n); + + Assert.assertEquals(index.count(column, 10), 0); + Assert.assertEquals(index.count(column, 20), 1); + Assert.assertEquals(index.countValues(column), 1); + Assert.assertEquals(index.countElements(column), 1); + } + + @Test + public void testGetIteratorNull() { + ColumnStore columnStore = generateEmptyNodeStore(); + columnStore.addColumn(new ColumnImpl("c", String.class, null, null, Origin.DATA, true, false)); + + IndexImpl index = columnStore.indexStore.mainIndex; + NodeImpl n = new NodeImpl(0); + index.put("c", null, n); + + Iterator itr = index.get("c", null).iterator(); + Assert.assertTrue(itr.hasNext()); + Assert.assertSame(itr.next(), n); + } + + @Test + public void testGetNullEntry() { + ColumnStore columnStore = generateEmptyNodeStore(); + columnStore.addColumn(new ColumnImpl("c", String.class, null, null, Origin.DATA, true, false)); + + IndexImpl index = columnStore.indexStore.mainIndex; + NodeImpl n = new NodeImpl(0); + index.put("c", null, n); + + Iterator>> itr = index.get(columnStore.getColumn("c")).iterator(); + Assert.assertTrue(itr.hasNext()); + Map.Entry> entry = itr.next(); + Assert.assertNull(entry.getKey()); + Assert.assertEquals(entry.getValue().size(), 1); + Assert.assertTrue(entry.getValue().contains(n)); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testGetNullEntrySetValue() { + ColumnStore columnStore = generateEmptyNodeStore(); + columnStore.addColumn(new ColumnImpl("c", String.class, null, null, Origin.DATA, true, false)); + + IndexImpl index = columnStore.indexStore.mainIndex; + NodeImpl n = new NodeImpl(0); + index.put("c", null, n); + + Iterator>> itr = index.get(columnStore.getColumn("c")).iterator(); + Assert.assertTrue(itr.hasNext()); + Map.Entry> entry = itr.next(); + entry.setValue(null); + } + + @Test + public void testNonNumberTypes() { + ColumnStore columnStore = generateEmptyNodeStore(); + columnStore.addColumn(new ColumnImpl("c1", String.class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c2", Boolean.class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c3", Character.class, null, null, Origin.DATA, true, false)); + + IndexImpl index = columnStore.indexStore.mainIndex; + NodeImpl n1 = new NodeImpl(0); + NodeImpl n2 = new NodeImpl(1); + + Object[] values = new Object[] { "foo", Boolean.TRUE, 'f' }; + + for (int i = 1; i <= values.length; i++) { + index.put("c" + i, values[i - 1], n1); + index.put("c" + i, null, n2); + } + + for (int i = 1; i <= values.length; i++) { + Column column = columnStore.getColumn("c" + i); + + Assert.assertEquals(index.countElements(column), 2); + Assert.assertEquals(index.countValues(column), 2); + + Assert.assertSame(getIterable(index.get("c" + i, values[i - 1]))[0], n1); + Assert.assertSame(getIterable(index.get("c" + i, null))[0], n2); + Assert.assertSame(getIterable(index.get(column, values[i - 1]))[0], n1); + Assert.assertSame(getIterable(index.get(column, null))[0], n2); + Assert.assertEquals(index.count("c" + i, values[i - 1]), 1); + Assert.assertEquals(index.count("c" + i, null), 1); + Assert.assertEquals(index.count(column, values[i - 1]), 1); + Assert.assertEquals(index.count(column, null), 1); + Assert.assertTrue(index.values(column).contains(null)); + Assert.assertTrue(index.values(column).contains(values[i - 1])); + } + + for (int i = 1; i <= values.length; i++) { + index.remove("c" + i, values[i - 1], n1); + index.remove("c" + i, null, n2); + } + + for (int i = 1; i <= values.length; i++) { + Column column = columnStore.getColumn("c" + i); + + Assert.assertEquals(index.countElements(column), 0); + Assert.assertEquals(index.countValues(column), 0); + } + } + + @Test + public void testPrimitiveNumberTypes() { + ColumnStore columnStore = generateEmptyNodeStore(); + columnStore.addColumn(new ColumnImpl("c1", Integer.class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c2", Short.class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c3", Float.class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c4", Double.class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c5", Long.class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c6", Byte.class, null, null, Origin.DATA, true, false)); + + IndexImpl index = columnStore.indexStore.mainIndex; + NodeImpl n1 = new NodeImpl(0); + NodeImpl n2 = new NodeImpl(1); + + Object[] values = new Object[] { 1, (short) 1, 1f, 1.0, 1l, (byte) 1 }; + + for (int i = 1; i <= values.length; i++) { + index.put("c" + i, values[i - 1], n1); + index.put("c" + i, null, n2); + } + + for (int i = 1; i <= values.length; i++) { + Column column = columnStore.getColumn("c" + i); + + Assert.assertEquals(index.countElements(column), 2); + Assert.assertEquals(index.countValues(column), 2); + + Assert.assertSame(getIterable(index.get("c" + i, values[i - 1]))[0], n1); + Assert.assertSame(getIterable(index.get("c" + i, null))[0], n2); + Assert.assertSame(getIterable(index.get(column, values[i - 1]))[0], n1); + Assert.assertSame(getIterable(index.get(column, null))[0], n2); + Assert.assertEquals(index.count("c" + i, values[i - 1]), 1); + Assert.assertEquals(index.count("c" + i, null), 1); + Assert.assertEquals(index.count(column, values[i - 1]), 1); + Assert.assertEquals(index.count(column, null), 1); + Assert.assertTrue(index.values(column).contains(null)); + Assert.assertTrue(index.values(column).contains(values[i - 1])); + + Number min = index.getMinValue(column); + Assert.assertEquals(min.byteValue(), (byte) 1); + Number max = index.getMaxValue(column); + Assert.assertEquals(max.byteValue(), (byte) 1); + } + + for (int i = 1; i <= values.length; i++) { + index.remove("c" + i, values[i - 1], n1); + index.remove("c" + i, null, n2); + } + + for (int i = 1; i <= values.length; i++) { + Column column = columnStore.getColumn("c" + i); + + Assert.assertEquals(index.countElements(column), 0); + Assert.assertEquals(index.countValues(column), 0); + } + } + + @Test + public void testArrayTypes() { + ColumnStore columnStore = generateEmptyNodeStore(); + columnStore.addColumn(new ColumnImpl("c1", int[].class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c2", short[].class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c3", float[].class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c4", double[].class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c5", long[].class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c6", byte[].class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c7", boolean[].class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c8", char[].class, null, null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("c9", String[].class, null, null, Origin.DATA, true, false)); + + IndexImpl index = columnStore.indexStore.mainIndex; + NodeImpl n1 = new NodeImpl(0); + NodeImpl n2 = new NodeImpl(1); + + Object[] values = new Object[9]; + values[0] = new int[] { 1, 2, 2 }; + values[1] = new short[] { 1, 2, 2 }; + values[2] = new float[] { 1, 2, 2 }; + values[3] = new double[] { 1, 2, 2 }; + values[4] = new long[] { 1, 2, 2 }; + values[5] = new byte[] { 1, 2, 2 }; + values[6] = new boolean[] { true, false, false }; + values[7] = new char[] { 1, 2, 2 }; + values[8] = new String[] { "foo", "bar", "bar" }; + + for (int i = 1; i <= values.length; i++) { + index.put("c" + i, values[i - 1], n1); + index.put("c" + i, null, n2); + } + + for (int i = 1; i <= values.length; i++) { + Column column = columnStore.getColumn("c" + i); + + Assert.assertEquals(index.countElements(column), 2); + Assert.assertEquals(index.countValues(column), 2); + + Assert.assertSame(getIterable(index.get("c" + i, values[i - 1]))[0], n1); + Assert.assertSame(getIterable(index.get("c" + i, null))[0], n2); + Assert.assertSame(getIterable(index.get(column, values[i - 1]))[0], n1); + Assert.assertSame(getIterable(index.get(column, null))[0], n2); + Assert.assertEquals(index.count("c" + i, values[i - 1]), 1); + Assert.assertEquals(index.count("c" + i, null), 1); + Assert.assertEquals(index.count(column, values[i - 1]), 1); + Assert.assertEquals(index.count(column, null), 1); + Assert.assertTrue(index.values(column).contains(null)); + Assert.assertTrue(index.values(column).contains(values[i - 1])); + } + + for (int i = 1; i <= values.length; i++) { + index.remove("c" + i, values[i - 1], n1); + index.remove("c" + i, null, n2); + } + + for (int i = 1; i <= values.length; i++) { + Column column = columnStore.getColumn("c" + i); + + Assert.assertEquals(index.countElements(column), 0); + Assert.assertEquals(index.countValues(column), 0); + } + } + + // UTILITIES + private NodeImpl[] generateNodesWithUniqueAttributes(IndexImpl index, boolean withNulls) { + int count = 100; + Random random = new Random(342); + NodeImpl[] nodes = new NodeImpl[count]; + for (int i = 0; i < 100; i++) { + NodeImpl n = new NodeImpl(i); + nodes[i] = n; + + for (Column col : index.columnStore) { + if (!col.isReadOnly()) { + if (withNulls && random.nextDouble() < 0.1) { + n.setAttribute(col, null); + } else if (col.getTypeClass().equals(String.class)) { + n.setAttribute(col, "" + i); + } else if (col.getTypeClass().equals(Integer.class)) { + n.setAttribute(col, i); + } else if (col.getTypeClass().equals(BigInteger.class)) { + n.setAttribute(col, BigInteger.valueOf(i)); + } + } + } + } + return nodes; + } + + private void putAll(NodeImpl[] nodes, IndexImpl index) { + for (NodeImpl n : nodes) { + for (Column col : index.columnStore) { + if (col.isIndexed()) { + Object val = n.getAttribute(col); + index.put(col, val, n); + } + } + } + } + + private IndexImpl generateEmptyIndex() { + ColumnStore columnStore = generateEmptyNodeStore(); + columnStore.addColumn(new ColumnImpl("foo", String.class, "foo", null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("age", Integer.class, "Age", null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl("big_int", BigInteger.class, "BigInt", null, Origin.DATA, true, false)); + return columnStore.indexStore.mainIndex; + } + + private ColumnStore generateEmptyNodeStore() { + GraphStore graphStore = new GraphStore(); + ColumnStore columnStore = graphStore.nodeTable.store; + return columnStore; + } + + private Node[] getIterable(Iterable itr) { + List list = new ArrayList<>(); + for (Node n : itr) { + list.add(n); + } + return list.toArray(new Node[0]); + } +} diff --git a/src/test/java/org/gephi/graph/impl/IndexImplTest.java b/src/test/java/org/gephi/graph/impl/IndexImplTest.java index 9337bc5e..1d7ef8ea 100644 --- a/src/test/java/org/gephi/graph/impl/IndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexImplTest.java @@ -13,37 +13,20 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectSet; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import org.gephi.graph.api.Column; -import org.gephi.graph.api.Origin; import org.gephi.graph.api.Node; +import org.gephi.graph.api.Origin; import org.testng.Assert; import org.testng.annotations.Test; -/** - * - * @author mbastian - */ public class IndexImplTest { @Test public void testIndexName() { - IndexImpl index = generateEmptyIndex(); + ColumnStore columnStore = generateEmptyNodeStore(); + IndexImpl index = columnStore.indexStore.mainIndex; Assert.assertEquals(index.getIndexClass(), Node.class); Assert.assertEquals(index.getIndexName(), "index_" + Node.class.getCanonicalName()); } @@ -55,10 +38,10 @@ public void testAddColumn() { ColumnImpl col = new ColumnImpl("foo", String.class, "foo", null, Origin.DATA, true, false); col.setStoreId(0); - Assert.assertEquals(index.size(), 0); + Assert.assertEquals(index.size(), GraphStoreConfiguration.NODE_DEFAULT_COLUMNS); index.addColumn(col); - Assert.assertEquals(index.size(), 1); - Assert.assertSame(index.getIndex(col).column, col); + Assert.assertEquals(index.size(), 1 + GraphStoreConfiguration.NODE_DEFAULT_COLUMNS); + Assert.assertSame(index.getIndex(col).getColumn(), col); } @Test @@ -74,7 +57,7 @@ public void testHasColumn() { index.addColumn(col1); index.addColumn(col2); Assert.assertTrue(index.hasColumn(col1)); - Assert.assertFalse(index.hasColumn(col2)); + Assert.assertTrue(index.hasColumn(col2)); } @Test @@ -108,605 +91,23 @@ public void testAddAllColumns() { col3.setStoreId(2); index.addAllColumns(new ColumnImpl[] { col1, col2, col3 }); - Assert.assertEquals(index.size(), 2); + Assert.assertEquals(index.size(), 3 + GraphStoreConfiguration.NODE_DEFAULT_COLUMNS); } @Test public void testDestroy() { ColumnStore columnStore = generateEmptyNodeStore(); IndexImpl index = columnStore.indexStore.mainIndex; - ColumnImpl col = new ColumnImpl("1", String.class, "1", null, Origin.DATA, true, false); - col.setStoreId(0); - index.addColumn(col); + ColumnImpl col1 = new ColumnImpl("1", String.class, "1", null, Origin.DATA, true, false); + ColumnImpl col2 = new ColumnImpl("2", String.class, "2", null, Origin.DATA, false, false); + col1.setStoreId(0); + col2.setStoreId(1); + + index.addAllColumns(new ColumnImpl[] { col1, col2 }); index.destroy(); Assert.assertEquals(index.size(), 0); - Assert.assertNull(index.getIndex(col)); - } - - @Test - public void testCount() { - IndexImpl index = generateEmptyIndex(); - NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); - putAll(nodes, index); - - for (Column col : index.columnStore) { - if (col.isIndexed()) { - Object2IntMap counter = new Object2IntOpenHashMap(); - for (NodeImpl n : nodes) { - Object obj = n.getAttribute(col); - counter.put(obj, counter.getInt(obj) + 1); - } - - for (Object2IntMap.Entry entry : counter.object2IntEntrySet()) { - int count = entry.getIntValue(); - Object val = entry.getKey(); - - Assert.assertEquals(index.count(col, val), count); - Assert.assertEquals(index.count(col.getId(), val), count); - } - } - } - } - - @Test - public void testGet() { - IndexImpl index = generateEmptyIndex(); - NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); - putAll(nodes, index); - - for (Column col : index.columnStore) { - if (col.isIndexed()) { - ObjectSet set = new ObjectOpenHashSet<>(); - for (NodeImpl n : nodes) { - Object obj = n.getAttribute(col); - set.add(obj); - } - - for (Object value : set) { - ObjectSet actual1 = new ObjectOpenHashSet(getIterable(index.get(col, value))); - ObjectSet actual2 = new ObjectOpenHashSet(getIterable(index.get(col.getId(), value))); - ObjectSet expected = new ObjectOpenHashSet(); - for (NodeImpl n : nodes) { - Object v = n.getAttribute(col); - if ((v == null && value == null) || (v != null && v.equals(value))) { - expected.add(n); - } - } - Assert.assertEquals(actual1, expected); - Assert.assertEquals(actual2, expected); - } - - for (Map.Entry> entry : index.get(col)) { - Object value = entry.getKey(); - Set actual = entry.getValue(); - - ObjectSet expected = new ObjectOpenHashSet(); - for (NodeImpl n : nodes) { - Object v = n.getAttribute(col); - if ((v == null && value == null) || (v != null && v.equals(value))) { - expected.add(n); - } - } - - Assert.assertEquals(actual, expected); - } - } - } - } - - @Test - public void testIsSortable() { - IndexImpl index = generateEmptyIndex(); - - Column ageCol = index.columnStore.getColumn("age"); - Column bigIntCol = index.columnStore.getColumn("big_int"); - Column fooCol = index.columnStore.getColumn("foo"); - - Assert.assertTrue(index.isSortable(ageCol)); - Assert.assertTrue(index.isSortable(bigIntCol)); - Assert.assertFalse(index.isSortable(fooCol)); - } - - @Test - public void testMinMaxValue() { - IndexImpl index = generateEmptyIndex(); - NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); - - Column ageCol = index.columnStore.getColumn("age"); - - Assert.assertNull(index.getMinValue(ageCol)); - Assert.assertNull(index.getMaxValue(ageCol)); - - putAll(nodes, index); - - int min = Integer.MAX_VALUE; - int max = Integer.MIN_VALUE; - for (NodeImpl n : nodes) { - Integer v = (Integer) n.getAttribute(ageCol); - if (v != null) { - min = Math.min(min, v); - max = Math.max(max, v); - } - } - - Assert.assertEquals(index.getMinValue(ageCol), min); - Assert.assertEquals(index.getMaxValue(ageCol), max); - } - - @Test - public void testMinMaxValueBigInteger() { - IndexImpl index = generateEmptyIndex(); - NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); - - Column bigIntCol = index.columnStore.getColumn("big_int"); - - Assert.assertNull(index.getMinValue(bigIntCol)); - Assert.assertNull(index.getMaxValue(bigIntCol)); - - putAll(nodes, index); - - BigInteger min = BigInteger.valueOf(Long.MAX_VALUE); - BigInteger max = BigInteger.valueOf(Long.MIN_VALUE); - for (NodeImpl n : nodes) { - BigInteger v = (BigInteger) n.getAttribute(bigIntCol); - if (v != null) { - if (v.compareTo(min) == -1) { - min = v; - } - if (v.compareTo(max) == 1) { - max = v; - } - } - } - - Assert.assertNotNull(index.getMinValue(bigIntCol)); - Assert.assertNotNull(index.getMaxValue(bigIntCol)); - - Assert.assertEquals(index.getMinValue(bigIntCol), min); - Assert.assertEquals(index.getMaxValue(bigIntCol), max); - } - - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testMinValueNoNumber() { - IndexImpl index = generateEmptyIndex(); - index.getMinValue(index.columnStore.getColumn("foo")); - } - - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testMaxValueNoNumber() { - IndexImpl index = generateEmptyIndex(); - index.getMaxValue(index.columnStore.getColumn("foo")); - } - - @Test - public void testValues() { - IndexImpl index = generateEmptyIndex(); - NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); - putAll(nodes, index); - - for (Column col : index.columnStore) { - if (col.isIndexed()) { - Collection collection = index.values(col); - - ObjectSet expected = new ObjectOpenHashSet<>(); - for (NodeImpl n : nodes) { - Object obj = n.getAttribute(col); - expected.add(obj); - } - - Assert.assertEquals(collection.size(), expected.size()); - Assert.assertEquals(index.countValues(col), expected.size()); - Assert.assertEquals(new ObjectOpenHashSet<>(collection), expected); - } - } - } - - @Test - public void testWithNullDecorator() { - IndexImpl index = generateEmptyIndex(); - Column ageColumn = index.columnStore.getColumn("age"); - Column fooColumn = index.columnStore.getColumn("foo"); - NodeImpl n1 = new NodeImpl(0); - NodeImpl n2 = new NodeImpl(1); - NodeImpl n3 = new NodeImpl(2); - index.put(ageColumn, 10, n1); - index.put(ageColumn, 20, n2); - index.put(fooColumn, null, n1); - index.put(fooColumn, "bar", n3); - - ColumnStandardIndexImpl withNullIndex = index.getIndex("foo"); - Collection withNullCollection = withNullIndex.values(); - Assert.assertEquals(withNullCollection.size(), 2); - Assert.assertFalse(withNullCollection.isEmpty()); - Assert.assertTrue(withNullCollection.contains(null)); - Assert.assertTrue(withNullCollection.contains("bar")); - Assert.assertFalse(withNullCollection.contains("none")); - Assert.assertEquals(withNullCollection.toArray(), new Object[] { null, "bar" }); - Assert.assertEquals(withNullCollection.toArray(new Object[0]), new Object[] { null, "bar" }); - Assert.assertTrue(withNullCollection.containsAll(Arrays.asList(new Object[] { null, "bar" }))); - Assert.assertFalse(withNullCollection.containsAll(Arrays.asList(new Object[] { null, "none" }))); - Iterator withNullItr = withNullCollection.iterator(); - Assert.assertTrue(withNullItr.hasNext()); - Assert.assertNull(withNullItr.next()); - Assert.assertTrue(withNullItr.hasNext()); - Assert.assertEquals(withNullItr.next(), "bar"); - Assert.assertFalse(withNullItr.hasNext()); - - ColumnStandardIndexImpl withoutNullIndex = index.getIndex("age"); - Collection withoutNullCollection = withoutNullIndex.values(); - Assert.assertEquals(withoutNullCollection.size(), 2); - Assert.assertFalse(withoutNullCollection.isEmpty()); - Assert.assertFalse(withoutNullCollection.contains(null)); - Assert.assertTrue(withoutNullCollection.contains(10)); - Assert.assertFalse(withoutNullCollection.contains(30)); - Assert.assertEquals(withoutNullCollection.toArray(), new Object[] { 10, 20 }); - Assert.assertEquals(withoutNullCollection.toArray(new Object[0]), new Object[] { 10, 20 }); - Assert.assertTrue(withoutNullCollection.containsAll(Arrays.asList(new Object[] { 10, 20 }))); - Assert.assertFalse(withoutNullCollection.containsAll(Arrays.asList(new Object[] { null }))); - Assert.assertFalse(withoutNullCollection.containsAll(Arrays.asList(new Object[] { 30 }))); - Iterator withoutNullItr = withoutNullCollection.iterator(); - Assert.assertTrue(withoutNullItr.hasNext()); - Assert.assertEquals(withoutNullItr.next(), 10); - Assert.assertTrue(withoutNullItr.hasNext()); - Assert.assertEquals(withoutNullItr.next(), 20); - Assert.assertFalse(withoutNullItr.hasNext()); - } - - @Test - public void testCountElements() { - IndexImpl index = generateEmptyIndex(); - - for (Column col : index.columnStore) { - if (col.isIndexed()) { - Assert.assertEquals(index.countElements(col), 0); - } - } - - NodeImpl[] nodes = generateNodesWithUniqueAttributes(index, true); - putAll(nodes, index); - - for (Column col : index.columnStore) { - if (col.isIndexed()) { - Assert.assertEquals(index.countElements(col), nodes.length); - } - } - } - - @Test - public void testPut() { - IndexImpl index = generateEmptyIndex(); - Column column = index.columnStore.getColumn("age"); - NodeImpl n = new NodeImpl(0); - - Integer v = 10; - Assert.assertSame(index.put(column, v, n), v); - Assert.assertEquals(index.count(column, v), 1); - - Assert.assertSame(index.put(column, v, n), v); - Assert.assertEquals(index.count(column, v), 1); - } - - @Test - public void testPutManagedValue() { - IndexImpl index = generateEmptyIndex(); - Column column = index.columnStore.getColumn("age"); - NodeImpl n1 = new NodeImpl(0); - NodeImpl n2 = new NodeImpl(1); - - Integer v = 10; - index.put(column, v, n1); - Assert.assertSame(index.put(column, 10, n2), v); - } - - @Test - public void testRemoveByColumn() { - IndexImpl index = generateEmptyIndex(); - Column column = index.columnStore.getColumn("age"); - NodeImpl n = new NodeImpl(0); - index.put(column, 10, n); - index.remove(column, 10, n); - - Assert.assertEquals(index.count(column, 10), 0); - } - - @Test - public void testRemoveByString() { - IndexImpl index = generateEmptyIndex(); - Column column = index.columnStore.getColumn("age"); - NodeImpl n = new NodeImpl(0); - index.put(column, 10, n); - index.remove("age", 10, n); - - Assert.assertEquals(index.count(column, 10), 0); - } - - @Test - public void testSetByColumn() { - IndexImpl index = generateEmptyIndex(); - Column column = index.columnStore.getColumn("age"); - NodeImpl n = new NodeImpl(0); - index.put(column, 10, n); - index.set(column, 10, 20, n); - - Assert.assertEquals(index.count(column, 10), 0); - Assert.assertEquals(index.count(column, 20), 1); - Assert.assertEquals(index.countValues(column), 1); - Assert.assertEquals(index.countElements(column), 1); - } - - @Test - public void testSetByString() { - IndexImpl index = generateEmptyIndex(); - Column column = index.columnStore.getColumn("age"); - NodeImpl n = new NodeImpl(0); - index.put(column, 10, n); - index.set("age", 10, 20, n); - - Assert.assertEquals(index.count(column, 10), 0); - Assert.assertEquals(index.count(column, 20), 1); - Assert.assertEquals(index.countValues(column), 1); - Assert.assertEquals(index.countElements(column), 1); - } - - @Test - public void testGetIteratorNull() { - ColumnStore columnStore = generateEmptyNodeStore(); - columnStore.addColumn(new ColumnImpl("c", String.class, null, null, Origin.DATA, true, false)); - - IndexImpl index = columnStore.indexStore.mainIndex; - NodeImpl n = new NodeImpl(0); - index.put("c", null, n); - - Iterator itr = index.get("c", null).iterator(); - Assert.assertTrue(itr.hasNext()); - Assert.assertSame(itr.next(), n); - } - - @Test - public void testGetNullEntry() { - ColumnStore columnStore = generateEmptyNodeStore(); - columnStore.addColumn(new ColumnImpl("c", String.class, null, null, Origin.DATA, true, false)); - - IndexImpl index = columnStore.indexStore.mainIndex; - NodeImpl n = new NodeImpl(0); - index.put("c", null, n); - - Iterator>> itr = index.get(columnStore.getColumn("c")).iterator(); - Assert.assertTrue(itr.hasNext()); - Entry> entry = itr.next(); - Assert.assertNull(entry.getKey()); - Assert.assertEquals(entry.getValue().size(), 1); - Assert.assertTrue(entry.getValue().contains(n)); - } - - @Test(expectedExceptions = UnsupportedOperationException.class) - public void testGetNullEntrySetValue() { - ColumnStore columnStore = generateEmptyNodeStore(); - columnStore.addColumn(new ColumnImpl("c", String.class, null, null, Origin.DATA, true, false)); - - IndexImpl index = columnStore.indexStore.mainIndex; - NodeImpl n = new NodeImpl(0); - index.put("c", null, n); - - Iterator>> itr = index.get(columnStore.getColumn("c")).iterator(); - Assert.assertTrue(itr.hasNext()); - Entry> entry = itr.next(); - entry.setValue(null); - } - - @Test - public void testNonNumberTypes() { - ColumnStore columnStore = generateEmptyNodeStore(); - columnStore.addColumn(new ColumnImpl("c1", String.class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c2", Boolean.class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c3", Character.class, null, null, Origin.DATA, true, false)); - - IndexImpl index = columnStore.indexStore.mainIndex; - NodeImpl n1 = new NodeImpl(0); - NodeImpl n2 = new NodeImpl(1); - - Object[] values = new Object[] { "foo", Boolean.TRUE, 'f' }; - - for (int i = 1; i <= values.length; i++) { - index.put("c" + i, values[i - 1], n1); - index.put("c" + i, null, n2); - } - - for (int i = 1; i <= values.length; i++) { - Column column = columnStore.getColumn("c" + i); - - Assert.assertEquals(index.countElements(column), 2); - Assert.assertEquals(index.countValues(column), 2); - - Assert.assertSame(getIterable(index.get("c" + i, values[i - 1]))[0], n1); - Assert.assertSame(getIterable(index.get("c" + i, null))[0], n2); - Assert.assertSame(getIterable(index.get(column, values[i - 1]))[0], n1); - Assert.assertSame(getIterable(index.get(column, null))[0], n2); - Assert.assertEquals(index.count("c" + i, values[i - 1]), 1); - Assert.assertEquals(index.count("c" + i, null), 1); - Assert.assertEquals(index.count(column, values[i - 1]), 1); - Assert.assertEquals(index.count(column, null), 1); - Assert.assertTrue(index.values(column).contains(null)); - Assert.assertTrue(index.values(column).contains(values[i - 1])); - } - - for (int i = 1; i <= values.length; i++) { - index.remove("c" + i, values[i - 1], n1); - index.remove("c" + i, null, n2); - } - - for (int i = 1; i <= values.length; i++) { - Column column = columnStore.getColumn("c" + i); - - Assert.assertEquals(index.countElements(column), 0); - Assert.assertEquals(index.countValues(column), 0); - } - } - - @Test - public void testPrimitiveNumberTypes() { - ColumnStore columnStore = generateEmptyNodeStore(); - columnStore.addColumn(new ColumnImpl("c1", Integer.class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c2", Short.class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c3", Float.class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c4", Double.class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c5", Long.class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c6", Byte.class, null, null, Origin.DATA, true, false)); - - IndexImpl index = columnStore.indexStore.mainIndex; - NodeImpl n1 = new NodeImpl(0); - NodeImpl n2 = new NodeImpl(1); - - Object[] values = new Object[] { 1, (short) 1, 1f, 1.0, 1l, (byte) 1 }; - - for (int i = 1; i <= values.length; i++) { - index.put("c" + i, values[i - 1], n1); - index.put("c" + i, null, n2); - } - - for (int i = 1; i <= values.length; i++) { - Column column = columnStore.getColumn("c" + i); - - Assert.assertEquals(index.countElements(column), 2); - Assert.assertEquals(index.countValues(column), 2); - - Assert.assertSame(getIterable(index.get("c" + i, values[i - 1]))[0], n1); - Assert.assertSame(getIterable(index.get("c" + i, null))[0], n2); - Assert.assertSame(getIterable(index.get(column, values[i - 1]))[0], n1); - Assert.assertSame(getIterable(index.get(column, null))[0], n2); - Assert.assertEquals(index.count("c" + i, values[i - 1]), 1); - Assert.assertEquals(index.count("c" + i, null), 1); - Assert.assertEquals(index.count(column, values[i - 1]), 1); - Assert.assertEquals(index.count(column, null), 1); - Assert.assertTrue(index.values(column).contains(null)); - Assert.assertTrue(index.values(column).contains(values[i - 1])); - - Number min = index.getMinValue(column); - Assert.assertEquals(min.byteValue(), (byte) 1); - Number max = index.getMaxValue(column); - Assert.assertEquals(max.byteValue(), (byte) 1); - } - - for (int i = 1; i <= values.length; i++) { - index.remove("c" + i, values[i - 1], n1); - index.remove("c" + i, null, n2); - } - - for (int i = 1; i <= values.length; i++) { - Column column = columnStore.getColumn("c" + i); - - Assert.assertEquals(index.countElements(column), 0); - Assert.assertEquals(index.countValues(column), 0); - } - } - - @Test - public void testArrayTypes() { - ColumnStore columnStore = generateEmptyNodeStore(); - columnStore.addColumn(new ColumnImpl("c1", int[].class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c2", short[].class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c3", float[].class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c4", double[].class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c5", long[].class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c6", byte[].class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c7", boolean[].class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c8", char[].class, null, null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("c9", String[].class, null, null, Origin.DATA, true, false)); - - IndexImpl index = columnStore.indexStore.mainIndex; - NodeImpl n1 = new NodeImpl(0); - NodeImpl n2 = new NodeImpl(1); - - Object[] values = new Object[9]; - values[0] = new int[] { 1, 2, 2 }; - values[1] = new short[] { 1, 2, 2 }; - values[2] = new float[] { 1, 2, 2 }; - values[3] = new double[] { 1, 2, 2 }; - values[4] = new long[] { 1, 2, 2 }; - values[5] = new byte[] { 1, 2, 2 }; - values[6] = new boolean[] { true, false, false }; - values[7] = new char[] { 1, 2, 2 }; - values[8] = new String[] { "foo", "bar", "bar" }; - - for (int i = 1; i <= values.length; i++) { - index.put("c" + i, values[i - 1], n1); - index.put("c" + i, null, n2); - } - - for (int i = 1; i <= values.length; i++) { - Column column = columnStore.getColumn("c" + i); - - Assert.assertEquals(index.countElements(column), 2); - Assert.assertEquals(index.countValues(column), 2); - - Assert.assertSame(getIterable(index.get("c" + i, values[i - 1]))[0], n1); - Assert.assertSame(getIterable(index.get("c" + i, null))[0], n2); - Assert.assertSame(getIterable(index.get(column, values[i - 1]))[0], n1); - Assert.assertSame(getIterable(index.get(column, null))[0], n2); - Assert.assertEquals(index.count("c" + i, values[i - 1]), 1); - Assert.assertEquals(index.count("c" + i, null), 1); - Assert.assertEquals(index.count(column, values[i - 1]), 1); - Assert.assertEquals(index.count(column, null), 1); - Assert.assertTrue(index.values(column).contains(null)); - Assert.assertTrue(index.values(column).contains(values[i - 1])); - } - - for (int i = 1; i <= values.length; i++) { - index.remove("c" + i, values[i - 1], n1); - index.remove("c" + i, null, n2); - } - - for (int i = 1; i <= values.length; i++) { - Column column = columnStore.getColumn("c" + i); - - Assert.assertEquals(index.countElements(column), 0); - Assert.assertEquals(index.countValues(column), 0); - } - } - - // UTILITIES - private NodeImpl[] generateNodesWithUniqueAttributes(IndexImpl index, boolean withNulls) { - int count = 100; - Random random = new Random(342); - NodeImpl[] nodes = new NodeImpl[count]; - for (int i = 0; i < 100; i++) { - NodeImpl n = new NodeImpl(i); - nodes[i] = n; - - for (Column col : index.columnStore) { - if (!col.isReadOnly()) { - if (withNulls && random.nextDouble() < 0.1) { - n.setAttribute(col, null); - } else if (col.getTypeClass().equals(String.class)) { - n.setAttribute(col, "" + i); - } else if (col.getTypeClass().equals(Integer.class)) { - n.setAttribute(col, i); - } else if (col.getTypeClass().equals(BigInteger.class)) { - n.setAttribute(col, BigInteger.valueOf(i)); - } - } - } - } - return nodes; - } - - private void putAll(NodeImpl[] nodes, IndexImpl index) { - for (NodeImpl n : nodes) { - for (Column col : index.columnStore) { - if (col.isIndexed()) { - Object val = n.getAttribute(col); - index.put(col, val, n); - } - } - } - } - - private IndexImpl generateEmptyIndex() { - ColumnStore columnStore = generateEmptyNodeStore(); - columnStore.addColumn(new ColumnImpl("foo", String.class, "foo", null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("age", Integer.class, "Age", null, Origin.DATA, true, false)); - columnStore.addColumn(new ColumnImpl("big_int", BigInteger.class, "BigInt", null, Origin.DATA, true, false)); - return columnStore.indexStore.mainIndex; + Assert.assertNull(index.getIndex(col1)); + Assert.assertNull(index.getIndex(col2)); } private ColumnStore generateEmptyNodeStore() { @@ -714,12 +115,4 @@ private ColumnStore generateEmptyNodeStore() { ColumnStore columnStore = graphStore.nodeTable.store; return columnStore; } - - private Node[] getIterable(Iterable itr) { - List list = new ArrayList<>(); - for (Node n : itr) { - list.add(n); - } - return list.toArray(new Node[0]); - } } diff --git a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java index 193b472e..a423c2ba 100644 --- a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java @@ -34,7 +34,7 @@ public void testEmpty() { ColumnStore columnStore = graphStore.nodeTable.store; IndexStore indexStore = columnStore.indexStore; IndexImpl mainIndex = indexStore.mainIndex; - Assert.assertEquals(mainIndex.size(), 0); + Assert.assertEquals(mainIndex.size(), GraphStoreConfiguration.NODE_DEFAULT_COLUMNS); } @Test From 5df7c53b5e11f380be9871b2ce897d37b7dd04fc Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 27 Dec 2021 20:12:03 +0100 Subject: [PATCH 047/271] Implement graph locking in ColumnNoIndex --- .../gephi/graph/impl/ColumnNoIndexImpl.java | 130 ++++++++++++------ .../org/gephi/graph/impl/TableImplTest.java | 3 +- 2 files changed, 87 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java index 263eb2ab..71836cb0 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java @@ -25,6 +25,7 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.Node; public class ColumnNoIndexImpl implements ColumnIndexImpl { @@ -35,11 +36,13 @@ public class ColumnNoIndexImpl implements ColumnIndexImpl< protected final Class elementClass; // Graph protected final Graph graph; + protected final GraphLock graphLock; protected ColumnNoIndexImpl(ColumnImpl column, Graph graph, Class elementClass) { this.column = column; this.elementClass = elementClass; this.graph = graph; + this.graphLock = graph.getLock(); } private Iterator getElementIterator() { @@ -53,20 +56,25 @@ private Iterator getElementIterator() { @Override public int count(K value) { - Iterator elementIterator = getElementIterator(); - int count = 0; - if (elementIterator != null) { - while (elementIterator.hasNext()) { - ElementImpl element = (ElementImpl) elementIterator.next(); - K obj = (K) element.getAttribute(column); - if (value == null && obj == null) { - count++; - } else if (value != null && value.equals(obj)) { - count++; + lock(); + try { + Iterator elementIterator = getElementIterator(); + int count = 0; + if (elementIterator != null) { + while (elementIterator.hasNext()) { + ElementImpl element = (ElementImpl) elementIterator.next(); + K obj = (K) element.getAttribute(column); + if (value == null && obj == null) { + count++; + } else if (value != null && value.equals(obj)) { + count++; + } } } + return count; + } finally { + unlock(); } - return count; } @Override @@ -76,16 +84,21 @@ public Iterable get(K value) { @Override public Collection values() { - Iterator elementIterator = getElementIterator(); - Set set = new ObjectOpenHashSet<>(); - if (elementIterator != null) { - while (elementIterator.hasNext()) { - ElementImpl element = (ElementImpl) elementIterator.next(); - K obj = (K) element.getAttribute(column); - set.add(obj); + lock(); + try { + Iterator elementIterator = getElementIterator(); + Set set = new ObjectOpenHashSet<>(); + if (elementIterator != null) { + while (elementIterator.hasNext()) { + ElementImpl element = (ElementImpl) elementIterator.next(); + K obj = (K) element.getAttribute(column); + set.add(obj); + } } + return set; + } finally { + unlock(); } - return set; } @Override @@ -113,22 +126,27 @@ public Number getMinValue() { if (!isSortable()) { throw new UnsupportedOperationException("Only supported for sortable columns"); } - Number min = null; - Iterator elementIterator = getElementIterator(); - if (elementIterator != null) { - double minN = Double.POSITIVE_INFINITY; - while (elementIterator.hasNext()) { - ElementImpl element = (ElementImpl) elementIterator.next(); - Number num = (Number) element.getAttribute(column); - if (min == null || (num != null && num.doubleValue() < minN)) { - if (num != null) { - minN = num.doubleValue(); + lock(); + try { + Number min = null; + Iterator elementIterator = getElementIterator(); + if (elementIterator != null) { + double minN = Double.POSITIVE_INFINITY; + while (elementIterator.hasNext()) { + ElementImpl element = (ElementImpl) elementIterator.next(); + Number num = (Number) element.getAttribute(column); + if (min == null || (num != null && num.doubleValue() < minN)) { + if (num != null) { + minN = num.doubleValue(); + } + min = num; } - min = num; } } + return min; + } finally { + unlock(); } - return min; } @Override @@ -136,23 +154,28 @@ public Number getMaxValue() { if (!isSortable()) { throw new UnsupportedOperationException("Only supported for sortable columns"); } - Number max = null; - Iterator elementIterator = getElementIterator(); - if (elementIterator != null) { - double maxN = Double.NEGATIVE_INFINITY; - - while (elementIterator.hasNext()) { - ElementImpl element = (ElementImpl) elementIterator.next(); - Number num = (Number) element.getAttribute(column); - if (max == null || (num != null && num.doubleValue() > maxN)) { - if (num != null) { - maxN = num.doubleValue(); + lock(); + try { + Number max = null; + Iterator elementIterator = getElementIterator(); + if (elementIterator != null) { + double maxN = Double.NEGATIVE_INFINITY; + + while (elementIterator.hasNext()) { + ElementImpl element = (ElementImpl) elementIterator.next(); + Number num = (Number) element.getAttribute(column); + if (max == null || (num != null && num.doubleValue() > maxN)) { + if (num != null) { + maxN = num.doubleValue(); + } + max = num; } - max = num; } } + return max; + } finally { + unlock(); } - return max; } @Override @@ -216,6 +239,7 @@ private class ElementWithValueIterator implements Iterator { public ElementWithValueIterator(Iterator itr, K value) { this.itr = itr; this.value = value; + lock(); } @Override @@ -227,7 +251,11 @@ public boolean hasNext() { pointer = element; } } - return pointer != null; + if (pointer != null) { + return true; + } + unlock(); + return false; } @Override @@ -242,4 +270,16 @@ public void remove() { throw new UnsupportedOperationException("Not supported."); } } + + private void lock() { + if (graphLock != null) { + graphLock.readLock(); + } + } + + private void unlock() { + if (graphLock != null) { + graphLock.readUnlock(); + } + } } diff --git a/src/test/java/org/gephi/graph/impl/TableImplTest.java b/src/test/java/org/gephi/graph/impl/TableImplTest.java index 26bd7a99..cc52e43c 100644 --- a/src/test/java/org/gephi/graph/impl/TableImplTest.java +++ b/src/test/java/org/gephi/graph/impl/TableImplTest.java @@ -103,7 +103,8 @@ public void testTypeClassCantBeNull2() { @Test public void testIsIndexed() { - TableImpl table = new TableImpl<>(Node.class, true); + GraphStore graphStore = new GraphStore(); + TableImpl table = new TableImpl<>(graphStore, Node.class, true); Column col1 = table.addColumn("Id", null, Integer.class, Origin.DATA, null, false); Column col2 = table.addColumn("1", null, Integer.class, Origin.DATA, null, true); From a3818f858afd042997ccafd32ea324be3559e429 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 27 Dec 2021 20:23:11 +0100 Subject: [PATCH 048/271] Add new utility countColumns method to Table --- src/main/java/org/gephi/graph/api/Table.java | 9 +++++++++ .../java/org/gephi/graph/impl/ColumnStore.java | 18 ++++++++++++++++++ .../java/org/gephi/graph/impl/TableImpl.java | 5 +++++ .../org/gephi/graph/impl/ColumnStoreTest.java | 13 +++++++++++++ 4 files changed, 45 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/Table.java b/src/main/java/org/gephi/graph/api/Table.java index d29e8ac1..7be566f1 100644 --- a/src/main/java/org/gephi/graph/api/Table.java +++ b/src/main/java/org/gephi/graph/api/Table.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.api; /** @@ -109,6 +110,14 @@ public interface Table extends ColumnIterable { */ public int countColumns(); + /** + * Counts the columns of the given origin. + * + * @param origin the origin + * @return the number of columns set with this origin + */ + public int countColumns(Origin origin); + /** * The element class of this column. * diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index f064385a..ad512d55 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -32,6 +32,7 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; import org.gephi.graph.api.Node; +import org.gephi.graph.api.Origin; public class ColumnStore implements ColumnIterable { @@ -341,6 +342,23 @@ public int size() { return length - garbageQueue.size(); } + public int size(Origin origin) { + checkNonNullObject(origin); + lock(); + try { + int res = 0; + for (int i = 0; i < length; i++) { + ColumnImpl c = columns[i]; + if (c != null && c.origin.equals(origin)) { + res++; + } + } + return res; + } finally { + unlock(); + } + } + protected TableObserverImpl createTableObserver(TableImpl table, boolean withDiff) { if (observers != null) { lock(); diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index 613a0862..a09b600a 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -96,6 +96,11 @@ public int countColumns() { return store.size(); } + @Override + public int countColumns(Origin origin) { + return store.size(origin); + } + @Override public int size() { return countColumns(); diff --git a/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java b/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java index 91ad792a..35d1cddb 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java @@ -266,6 +266,19 @@ public void testHasColumn() { Assert.assertFalse(store.hasColumn("A")); } + @Test + public void testSizeByOrigin() { + ColumnStore store = new ColumnStore(Node.class, false); + ColumnImpl col1 = new ColumnImpl("0", Integer.class, null, null, Origin.DATA, false, false); + store.addColumn(col1); + + ColumnImpl col2 = new ColumnImpl("1", Integer.class, null, null, Origin.PROPERTY, false, false); + store.addColumn(col2); + + Assert.assertEquals(store.size(Origin.DATA), 1); + Assert.assertEquals(store.size(Origin.PROPERTY), 1); + } + @Test public void testHasColumnDifferentCase() { ColumnStore store = new ColumnStore(Node.class, false); From d3768eedd671d78115be1774c4b796ff25df75e4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 22 Jan 2022 15:47:10 +0100 Subject: [PATCH 049/271] Improve locking to fix #129, also adding toSet as utility --- .../org/gephi/graph/api/EdgeIterable.java | 14 + .../org/gephi/graph/api/ElementIterable.java | 13 + .../org/gephi/graph/api/NodeIterable.java | 14 + .../graph/impl/ColumnStandardIndexImpl.java | 135 +++---- .../java/org/gephi/graph/impl/EdgeStore.java | 17 + .../graph/impl/ElementIterableWrapper.java | 14 +- .../gephi/graph/impl/GraphAttributesImpl.java | 1 - .../java/org/gephi/graph/impl/GraphStore.java | 3 +- .../java/org/gephi/graph/impl/IndexImpl.java | 1 - .../java/org/gephi/graph/impl/IndexStore.java | 14 +- .../gephi/graph/impl/IntervalIndexImpl.java | 149 ++++---- .../gephi/graph/impl/IntervalIndexStore.java | 2 +- .../java/org/gephi/graph/impl/NodeStore.java | 19 + .../org/gephi/graph/impl/NodesQuadTree.java | 12 + .../org/gephi/graph/impl/TimeIndexImpl.java | 120 +++--- .../org/gephi/graph/impl/TimeIndexStore.java | 344 +++++++++++------- .../java/org/gephi/graph/impl/TimeStore.java | 10 +- .../gephi/graph/impl/TimestampIndexImpl.java | 142 ++++---- .../gephi/graph/impl/TimestampIndexStore.java | 2 +- .../org/gephi/graph/impl/BasicGraphStore.java | 33 +- .../org/gephi/graph/impl/EdgeImplTest.java | 8 + .../org/gephi/graph/impl/TimeStoreTest.java | 22 +- .../graph/impl/TimestampIndexImplTest.java | 12 +- 23 files changed, 660 insertions(+), 441 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/EdgeIterable.java b/src/main/java/org/gephi/graph/api/EdgeIterable.java index 69a243e7..399c848e 100644 --- a/src/main/java/org/gephi/graph/api/EdgeIterable.java +++ b/src/main/java/org/gephi/graph/api/EdgeIterable.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Set; /** * An edge iterable. @@ -54,6 +55,14 @@ public interface EdgeIterable extends ElementIterable { @Override public Collection toCollection(); + /** + * Returns the iterator content as a set. + * + * @return edge set + */ + @Override + public Set toSet(); + /** * Empty edge iterable. */ @@ -89,6 +98,11 @@ public Collection toCollection() { return Collections.EMPTY_LIST; } + @Override + public Set toSet() { + return Collections.EMPTY_SET; + } + @Override public void doBreak() { } diff --git a/src/main/java/org/gephi/graph/api/ElementIterable.java b/src/main/java/org/gephi/graph/api/ElementIterable.java index 09eb3eb6..a3ec3e27 100644 --- a/src/main/java/org/gephi/graph/api/ElementIterable.java +++ b/src/main/java/org/gephi/graph/api/ElementIterable.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Set; /** * Element iterable. @@ -54,6 +55,13 @@ public interface ElementIterable extends Iterable { */ public Collection toCollection(); + /** + * Returns the iterator content as a set. + * + * @return element set + */ + public Set toSet(); + /** * Break the iterator and release read lock (if any). */ @@ -94,6 +102,11 @@ public Collection toCollection() { return Collections.EMPTY_LIST; } + @Override + public Set toSet() { + return Collections.EMPTY_SET; + } + @Override public void doBreak() { } diff --git a/src/main/java/org/gephi/graph/api/NodeIterable.java b/src/main/java/org/gephi/graph/api/NodeIterable.java index fab69126..88601682 100644 --- a/src/main/java/org/gephi/graph/api/NodeIterable.java +++ b/src/main/java/org/gephi/graph/api/NodeIterable.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Set; /** * A node iterable. @@ -54,6 +55,14 @@ public interface NodeIterable extends ElementIterable { @Override public Collection toCollection(); + /** + * Returns the iterator content as a set. + * + * @return node set + */ + @Override + public Set toSet(); + /** * Empty node iterable. */ @@ -89,6 +98,11 @@ public Collection toCollection() { return Collections.EMPTY_LIST; } + @Override + public Set toSet() { + return Collections.EMPTY_SET; + } + @Override public void doBreak() { } diff --git a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java index 8255526d..033d5569 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java @@ -58,47 +58,53 @@ public abstract class ColumnStandardIndexImpl implements C protected ColumnStandardIndexImpl(ColumnImpl column) { this.column = column; this.nullSet = new ValueSet<>(null); - if (column.table != null) { - lock = column.table.getLock(); - } else { - lock = null; - } + this.lock = GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? new TableLockImpl() : null; } @Override public K putValue(T element, K value) { - if (value == null) { - if (nullSet.add(element)) { - elements++; - } - } else { - ValueSet set = getValueSet(value); - if (set == null) { - set = addValue(value); - } - value = set.value; + lock(); + try { + if (value == null) { + if (nullSet.add(element)) { + elements++; + } + } else { + ValueSet set = getValueSet(value); + if (set == null) { + set = addValue(value); + } + value = set.value; - if (set.add(element)) { - elements++; + if (set.add(element)) { + elements++; + } } + } finally { + unlock(); } return value; } @Override public void removeValue(T element, K value) { - if (value == null) { - if (nullSet.remove(element)) { - elements--; - } - } else { - ValueSet set = getValueSet(value); - if (set.remove(element)) { - elements--; - } - if (set.isEmpty()) { - removeValue(value); + lock(); + try { + if (value == null) { + if (nullSet.remove(element)) { + elements--; + } + } else { + ValueSet set = getValueSet(value); + if (set.remove(element)) { + elements--; + } + if (set.isEmpty()) { + removeValue(value); + } } + } finally { + unlock(); } } @@ -142,64 +148,68 @@ public Collection values() { @Override public int countValues() { - lock(); - try { - return (nullSet.isEmpty() ? 0 : 1) + map.size(); - } finally { - unlock(); - } + return (nullSet.isEmpty() ? 0 : 1) + map.size(); } @Override public int countElements() { - lock(); - try { - return elements; - } finally { - unlock(); - } + return elements; } @Override public Number getMinValue() { - if (isSortable()) { - if (map.isEmpty()) { - return null; + lock(); + try { + if (isSortable()) { + if (map.isEmpty()) { + return null; + } else { + return (Number) ((SortedMap) map).firstKey(); + } } else { - return (Number) ((SortedMap) map).firstKey(); + throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column + .getTypeClass().getSimpleName() + ")."); } - } else { - throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column - .getTypeClass().getSimpleName() + ")."); + } finally { + unlock(); } } @Override public Number getMaxValue() { - if (isSortable()) { - if (map.isEmpty()) { - return null; + lock(); + try { + if (isSortable()) { + if (map.isEmpty()) { + return null; + } else { + return (Number) ((SortedMap) map).lastKey(); + } } else { - return (Number) ((SortedMap) map).lastKey(); + throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column + .getTypeClass().getSimpleName() + ")."); } - } else { - throw new UnsupportedOperationException("'" + column.getId() + "' is not a sortable column (" + column - .getTypeClass().getSimpleName() + ")."); + } finally { + unlock(); } } @Override public void destroy() { + lock(); map = null; nullSet.clear(); elements = 0; + unlock(); } @Override public void clear() { + lock(); map.clear(); nullSet.clear(); elements = 0; + unlock(); } @Override @@ -559,7 +569,6 @@ public Object[] toArray() { @Override public V[] toArray(V[] array) { - if (hasNull()) { if (array.length < size()) { array = (V[]) java.lang.reflect.Array @@ -703,25 +712,25 @@ public Set setValue(Set v) { } } - private class LockableIterable implements Iterable { + private class LockableIterable implements Iterable { - private final Iterable ite; + private final Iterable ite; - public LockableIterable(Iterable ite) { + public LockableIterable(Iterable ite) { this.ite = ite; } @Override - public Iterator iterator() { + public Iterator iterator() { return new LockableIterator<>(ite.iterator()); } } - private class LockableIterator implements Iterator { + private class LockableIterator implements Iterator { - private final Iterator itr; + private final Iterator itr; - public LockableIterator(Iterator itr) { + public LockableIterator(Iterator itr) { this.itr = itr; } @@ -735,7 +744,7 @@ public boolean hasNext() { } @Override - public T next() { + public E next() { return itr.next(); } diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 2b1e9314..3a2cb7d9 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -24,8 +24,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Node; @@ -847,6 +849,21 @@ public Collection toCollection() { return list; } + @Override + public Set toSet() { + readLock(); + + Set set = new HashSet<>(size); + EdgeStoreIterator itr = iterator(); + while (itr.hasNext()) { + EdgeImpl n = itr.next(); + set.add(n); + } + + readUnlock(); + return set; + } + @Override public boolean containsAll(Collection c) { checkCollection(c); diff --git a/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java b/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java index d4650934..596fa89b 100644 --- a/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java @@ -17,8 +17,10 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import org.gephi.graph.api.Element; import org.gephi.graph.api.ElementIterable; @@ -42,18 +44,28 @@ public Iterator iterator() { } protected T[] toArray(T[] a) { + // TODO This can be improved return toCollection().toArray(a); } @Override public Collection toCollection() { List list = new ArrayList<>(); - for (; iterator.hasNext();) { + while (iterator.hasNext()) { list.add(iterator.next()); } return list; } + @Override + public Set toSet() { + Set set = new HashSet<>(); + while (iterator.hasNext()) { + set.add(iterator.next()); + } + return set; + } + @Override public void doBreak() { if (lock != null) { diff --git a/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java b/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java index c0447508..3ffc08bd 100644 --- a/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java @@ -21,7 +21,6 @@ import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Interval; import org.gephi.graph.api.types.TimeMap; -import org.gephi.graph.api.types.TimestampMap; import org.gephi.graph.impl.utils.MapDeepEquals; public class GraphAttributesImpl { diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index d5d3d7d7..8a50fdce 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -93,8 +93,7 @@ public GraphStore(GraphModelImpl model) { viewStore, GraphStoreConfiguration.ENABLE_OBSERVERS ? version : null); nodeTable = new TableImpl<>(this, Node.class, GraphStoreConfiguration.ENABLE_INDEX_NODES); edgeTable = new TableImpl<>(this, Edge.class, GraphStoreConfiguration.ENABLE_INDEX_EDGES); - timeStore = new TimeStore(this, GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? lock : null, - GraphStoreConfiguration.ENABLE_INDEX_TIMESTAMP); + timeStore = new TimeStore(this, GraphStoreConfiguration.ENABLE_INDEX_TIMESTAMP); attributes = new GraphAttributesImpl(); factory = new GraphFactoryImpl(this); timeFormat = GraphStoreConfiguration.DEFAULT_TIME_FORMAT; diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index c6c42b82..bcac3cff 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -23,7 +23,6 @@ import org.gephi.graph.api.ColumnIndex; import org.gephi.graph.api.Element; import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Index; public class IndexImpl implements Index { diff --git a/src/main/java/org/gephi/graph/impl/IndexStore.java b/src/main/java/org/gephi/graph/impl/IndexStore.java index 818b3a2a..e9bb6996 100644 --- a/src/main/java/org/gephi/graph/impl/IndexStore.java +++ b/src/main/java/org/gephi/graph/impl/IndexStore.java @@ -178,7 +178,7 @@ public void index(T element) { } public void indexView(Graph graph) { - IndexImpl viewIndex = viewIndexes.get(graph.getView()); + final IndexImpl viewIndex = viewIndexes.get(graph.getView()); if (viewIndex != null) { graph.readLock(); try { @@ -224,8 +224,10 @@ public void indexInView(T element, GraphView view) { for (int i = 0; i < length; i++) { Column c = cols[i]; if (c != null && c.isIndexed()) { - Object value = elementImpl.attributes[c.getIndex()]; - index.put(c, value, element); + synchronized (elementImpl) { + Object value = elementImpl.attributes[c.getIndex()]; + index.put(c, value, element); + } } } } @@ -245,8 +247,10 @@ public void clearInView(T element, GraphView view) { for (int i = 0; i < length; i++) { Column c = cols[i]; if (c != null && c.isIndexed()) { - Object value = elementImpl.attributes[c.getIndex()]; - index.remove(c, value, element); + synchronized (elementImpl) { + Object value = elementImpl.attributes[c.getIndex()]; + index.remove(c, value, element); + } } } } diff --git a/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java b/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java index 7a3952a9..75034b32 100644 --- a/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java @@ -32,104 +32,119 @@ public IntervalIndexImpl(TimeIndexStore @Override public double getMinTimestamp() { - if (mainIndex) { + lock(); + try { Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; - if (!sortedMap.isEmpty()) { - return sortedMap.getLow(); - } - } else { - Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; - if (!sortedMap.isEmpty()) { - for (Map.Entry entry : sortedMap.entrySet()) { - int index = entry.getValue(); - if (index < timestamps.length) { - TimeIndexEntry intervalEntry = timestamps[index]; - if (intervalEntry != null) { - return entry.getKey().getLow(); + if (mainIndex) { + if (!sortedMap.isEmpty()) { + return sortedMap.getLow(); + } + } else { + if (!sortedMap.isEmpty()) { + for (Map.Entry entry : sortedMap.entrySet()) { + int index = entry.getValue(); + if (index < timestamps.length) { + TimeIndexEntry intervalEntry = timestamps[index]; + if (intervalEntry != null) { + return entry.getKey().getLow(); + } } } } } + return Double.NEGATIVE_INFINITY; + } finally { + unlock(); } - return Double.NEGATIVE_INFINITY; } @Override public double getMaxTimestamp() { - if (mainIndex) { - Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; - if (!sortedMap.isEmpty()) { - return sortedMap.getHigh(); - } - } else { - // TODO Better algorithm to find max - Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; - if (!sortedMap.isEmpty()) { - double max = Double.NEGATIVE_INFINITY; - boolean found = false; - for (Map.Entry entry : sortedMap.entrySet()) { - int index = entry.getValue(); - if (index < timestamps.length) { - TimeIndexEntry intervalEntry = timestamps[index]; - if (intervalEntry != null) { - found = true; - max = Math.max(max, entry.getKey().getHigh()); + lock(); + try { + if (mainIndex) { + Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; + if (!sortedMap.isEmpty()) { + return sortedMap.getHigh(); + } + } else { + // TODO Better algorithm to find max + Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; + if (!sortedMap.isEmpty()) { + double max = Double.NEGATIVE_INFINITY; + boolean found = false; + for (Map.Entry entry : sortedMap.entrySet()) { + int index = entry.getValue(); + if (index < timestamps.length) { + TimeIndexEntry intervalEntry = timestamps[index]; + if (intervalEntry != null) { + found = true; + max = Math.max(max, entry.getKey().getHigh()); + } } } + if (found) { + return max; + } } - if (found) { - return max; - } - } + } + return Double.POSITIVE_INFINITY; + } finally { + unlock(); } - return Double.POSITIVE_INFINITY; } @Override - public ElementIterable get(double timestamp) { + public ElementIterable get(double timestamp) { checkDouble(timestamp); - readLock(); - ObjectSet elements = new ObjectOpenHashSet<>(); - Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; - if (!sortedMap.isEmpty()) { - for (Integer index : sortedMap.values(timestamp)) { - if (index < timestamps.length) { - TimeIndexEntry ts = timestamps[index]; - if (ts != null) { - elements.addAll(ts.elementSet); + lock(); + try { + ObjectSet elements = new ObjectOpenHashSet<>(); + Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; + if (!sortedMap.isEmpty()) { + for (Integer index : sortedMap.values(timestamp)) { + if (index < timestamps.length) { + TimeIndexEntry ts = timestamps[index]; + if (ts != null) { + elements.addAll(ts.elementSet); + } } } } + if (!elements.isEmpty()) { + return new ElementSetWrapperIterable(elements); + } + return ElementIterable.EMPTY; + } finally { + unlock(); } - if (!elements.isEmpty()) { - return new ElementIterableImpl(new ElementIteratorImpl(elements.iterator())); - } - readUnlock(); - return ElementIterable.EMPTY; } @Override - public ElementIterable get(Interval interval) { + public ElementIterable get(Interval interval) { - readLock(); - ObjectSet elements = new ObjectOpenHashSet<>(); - Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; - if (!sortedMap.isEmpty()) { - for (Integer index : sortedMap.values(interval)) { - if (index < timestamps.length) { - TimeIndexEntry ts = timestamps[index]; - if (ts != null) { - elements.addAll(ts.elementSet); + lock(); + try { + ObjectSet elements = new ObjectOpenHashSet<>(); + Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; + if (!sortedMap.isEmpty()) { + for (Integer index : sortedMap.values(interval)) { + if (index < timestamps.length) { + TimeIndexEntry ts = timestamps[index]; + if (ts != null) { + elements.addAll(ts.elementSet); + } } } } + if (!elements.isEmpty()) { + return new ElementSetWrapperIterable(elements); + } + return ElementIterable.EMPTY; + } finally { + unlock(); } - if (!elements.isEmpty()) { - return new ElementIterableImpl(new ElementIteratorImpl(elements.iterator())); - } - readUnlock(); - return ElementIterable.EMPTY; } } diff --git a/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java b/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java index 86f0063a..cc194098 100644 --- a/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/IntervalIndexStore.java @@ -22,7 +22,7 @@ public class IntervalIndexStore extends TimeIndexStore> { - public IntervalIndexStore(Class type, GraphLockImpl lock, boolean indexed) { + public IntervalIndexStore(Class type, TableLockImpl lock, boolean indexed) { super(type, lock, indexed, new Interval2IntTreeMap()); mainIndex = indexed ? new IntervalIndexImpl(this, true) : null; } diff --git a/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java index b6bc47e8..9a408885 100644 --- a/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -20,8 +20,10 @@ import it.unimi.dsi.fastutil.objects.ObjectSet; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; @@ -231,6 +233,23 @@ public Collection toCollection() { return list; } + @Override + public Set toSet() { + readLock(); + + Set set = new HashSet<>(size); + + NodeStoreIterator itr = iterator(); + while (itr.hasNext()) { + NodeImpl n = itr.next(); + set.add(n); + } + + readUnlock(); + + return set; + } + @Override public boolean add(final Node n) { checkNonNullNodeObject(n); diff --git a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index 1a95ffea..a31746ef 100644 --- a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Deque; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; @@ -518,6 +519,17 @@ public Collection toCollection() { return list; } + @Override + public Set toSet() { + final Set set = new HashSet<>(); + + for (Node node : this) { + set.add(node); + } + + return set; + } + @Override public void doBreak() { readUnlock(); diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java index cdc55a63..e3aa2a00 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java @@ -20,8 +20,10 @@ import it.unimi.dsi.fastutil.objects.ObjectSet; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import org.gephi.graph.api.Element; import org.gephi.graph.api.ElementIterable; import org.gephi.graph.api.TimeIndex; @@ -31,7 +33,7 @@ public abstract class TimeIndexImpl, M extends TimeMap> implements TimeIndex { // Data - protected final GraphLockImpl lock; + protected final TableLockImpl lock; protected final TimeIndexStore timestampIndexStore; protected final boolean mainIndex; protected TimeIndexEntry[] timestamps; @@ -41,7 +43,7 @@ protected TimeIndexImpl(TimeIndexStore store, boolean main) { timestampIndexStore = store; mainIndex = main; timestamps = new TimeIndexEntry[0]; - lock = store.graphLock; + lock = store.lock; } public boolean hasElements() { @@ -49,32 +51,44 @@ public boolean hasElements() { } public void clear() { + lock(); timestamps = new TimeIndexEntry[0]; elementCount = 0; + unlock(); } protected void add(int timestampIndex, Element element) { - ensureArraySize(timestampIndex); - TimeIndexEntry entry = timestamps[timestampIndex]; - if (entry == null) { - entry = addTimestamp(timestampIndex); - } - if (entry.add(element)) { - elementCount++; + lock(); + try { + ensureArraySize(timestampIndex); + TimeIndexEntry entry = timestamps[timestampIndex]; + if (entry == null) { + entry = addTimestamp(timestampIndex); + } + if (entry.add(element)) { + elementCount++; + } + } finally { + unlock(); } } protected void remove(int timestampIndex, Element element) { - TimeIndexEntry entry = timestamps[timestampIndex]; - if (entry.remove(element)) { - elementCount--; - if (entry.isEmpty()) { - clearEntry(timestampIndex); + lock(); + try { + TimeIndexEntry entry = timestamps[timestampIndex]; + if (entry.remove(element)) { + elementCount--; + if (entry.isEmpty()) { + clearEntry(timestampIndex); + } } + } finally { + unlock(); } } - protected TimeIndexEntry addTimestamp(final int index) { + private TimeIndexEntry addTimestamp(final int index) { ensureArraySize(index); TimeIndexEntry entry = new TimeIndexEntry(); timestamps[index] = entry; @@ -99,27 +113,15 @@ protected void checkDouble(double timestamp) { } } - protected void readLock() { - if (lock != null) { - lock.readLock(); - } - } - - protected void readUnlock() { - if (lock != null) { - lock.readUnlock(); - } - } - - protected void writeLock() { + protected void lock() { if (lock != null) { - lock.writeLock(); + lock.lock(); } } - protected void writeUnlock() { + protected void unlock() { if (lock != null) { - lock.writeUnlock(); + lock.unlock(); } } @@ -144,68 +146,36 @@ public boolean isEmpty() { } } - protected class ElementIteratorImpl implements Iterator { - - private final ObjectIterator itr; + protected class ElementSetWrapperIterable implements ElementIterable { - public ElementIteratorImpl(ObjectIterator itr) { - this.itr = itr; - } + protected final Set set; - @Override - public boolean hasNext() { - final boolean hasNext = itr.hasNext(); - if (!hasNext) { - readUnlock(); - } - return hasNext; - } - - @Override - public Element next() { - return itr.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Not supported."); - } - } - - protected class ElementIterableImpl implements ElementIterable { - - protected final Iterator iterator; - - public ElementIterableImpl(Iterator iterator) { - this.iterator = iterator; + public ElementSetWrapperIterable(Set set) { + this.set = set; } @Override public Iterator iterator() { - return iterator; + return set.iterator(); } @Override public Element[] toArray() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list.toArray(new Element[0]); + return set.toArray(new Element[0]); } @Override public Collection toCollection() { - List list = new ArrayList<>(); - for (; iterator.hasNext();) { - list.add(iterator.next()); - } - return list; + return set; + } + + @Override + public Set toSet() { + return set; } @Override public void doBreak() { - readUnlock(); } } } diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java index 9de8e69e..e4958068 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java @@ -35,10 +35,10 @@ public abstract class TimeIndexStore, M extends TimeMap> { // Lock - protected final GraphLockImpl graphLock; + protected final TableLockImpl lock; // Element protected final Class elementType; - // Timestamp index managament + // Timestamp index management protected final Map timeSortedMap; protected final IntSortedSet garbageQueue; protected int[] countMap; @@ -47,9 +47,9 @@ public abstract class TimeIndexStore, protected TimeIndexImpl mainIndex; protected final Map viewIndexes; - protected TimeIndexStore(Class type, GraphLockImpl lock, boolean indexed, Map sortedMap) { - elementType = type; - graphLock = lock; + protected TimeIndexStore(Class type, TableLockImpl lock, boolean indexed, Map sortedMap) { + this.elementType = type; + this.lock = lock; garbageQueue = new IntRBTreeSet(); // Subclass @@ -68,44 +68,54 @@ protected TimeIndexStore(Class type, GraphLockImpl lock, boolean indexed, Map public Integer add(K k) { checkK(k); - Integer id = timeSortedMap.get(k); - if (id == null) { - if (!garbageQueue.isEmpty()) { - id = garbageQueue.firstInt(); - garbageQueue.remove(id); + lock(); + try { + Integer id = timeSortedMap.get(k); + if (id == null) { + if (!garbageQueue.isEmpty()) { + id = garbageQueue.firstInt(); + garbageQueue.remove(id); + } else { + id = length++; + } + timeSortedMap.put(k, id); + ensureArraySize(id); + countMap[id] = 1; } else { - id = length++; + countMap[id]++; } - timeSortedMap.put(k, id); - ensureArraySize(id); - countMap[id] = 1; - } else { - countMap[id]++; - } - return id; + return id; + } finally { + unlock(); + } } public int add(K k, Element element) { - int timeIndex = add(k); - - if (mainIndex != null) { - mainIndex.add(timeIndex, element); - - if (!viewIndexes.isEmpty()) { - for (Entry entry : viewIndexes.entrySet()) { - GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); - DirectedSubgraph graph = graphView.getDirectedGraph(); - boolean node = element instanceof Node; - if (node ? graph.contains((Node) element) : graph.contains((Edge) element)) { - entry.getValue().add(timeIndex, element); + lock(); + try { + int timeIndex = add(k); + + if (mainIndex != null) { + mainIndex.add(timeIndex, element); + + if (!viewIndexes.isEmpty()) { + for (Entry entry : viewIndexes.entrySet()) { + GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); + DirectedSubgraph graph = graphView.getDirectedGraph(); + boolean node = element instanceof Node; + if (node ? graph.contains((Node) element) : graph.contains((Edge) element)) { + entry.getValue().add(timeIndex, element); + } } - } + } } - } - return timeIndex; + return timeIndex; + } finally { + unlock(); + } } public void add(TimeMap timeMap) { @@ -121,41 +131,51 @@ public void add(TimeSet timeSet) { } public Integer remove(K k) { - checkK(k); - - Integer id = timeSortedMap.get(k); - if (id != null) { - if (--countMap[id] == 0) { - garbageQueue.add(id); - timeSortedMap.remove(k); + lock(); + try { + checkK(k); + + Integer id = timeSortedMap.get(k); + if (id != null) { + if (--countMap[id] == 0) { + garbageQueue.add(id); + timeSortedMap.remove(k); + } } + return id; + } finally { + unlock(); } - return id; } public int remove(K k, Element element) { - Integer timeIndex = remove(k); - checkTimeIndex(timeIndex); - - if (mainIndex != null) { - mainIndex.remove(timeIndex, element); - - if (!viewIndexes.isEmpty()) { - for (Entry entry : viewIndexes.entrySet()) { - GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); - DirectedSubgraph graph = graphView.getDirectedGraph(); - if (element instanceof Node) { - if (graph.contains((Node) element)) { + lock(); + try { + Integer timeIndex = remove(k); + checkTimeIndex(timeIndex); + + if (mainIndex != null) { + mainIndex.remove(timeIndex, element); + + if (!viewIndexes.isEmpty()) { + for (Entry entry : viewIndexes.entrySet()) { + GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); + DirectedSubgraph graph = graphView.getDirectedGraph(); + if (element instanceof Node) { + if (graph.contains((Node) element)) { + entry.getValue().remove(timeIndex, element); + } + } else if (graph.contains((Edge) element)) { entry.getValue().remove(timeIndex, element); } - } else if (graph.contains((Edge) element)) { - entry.getValue().remove(timeIndex, element); } } } - } - return timeIndex; + return timeIndex; + } finally { + unlock(); + } } public void remove(M timeMap) { @@ -173,85 +193,109 @@ public void remove(S timeSet) { public boolean contains(K k) { checkK(k); - return timeSortedMap.containsKey(k); + lock(); + try { + return timeSortedMap.containsKey(k); + } finally { + unlock(); + } } public void index(Element element) { - S timeSet = getTimeSet(element); + lock(); + try { + S timeSet = getTimeSet(element); - if (timeSet != null) { - add(timeSet); - } + if (timeSet != null) { + add(timeSet); + } - for (Object val : element.getAttributes()) { - if (val != null && val instanceof TimeMap) { - TimeMap dynamicValue = (TimeMap) val; - add(dynamicValue); + synchronized (element) { + for (Object val : element.getAttributes()) { + if (val instanceof TimeMap) { + TimeMap dynamicValue = (TimeMap) val; + add(dynamicValue); + } + } } - } - if (timeSet != null && mainIndex != null) { - K[] ts = timeSet.toArray(); - int tsLength = ts.length; - for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - mainIndex.add(timestampIndex, element); + if (timeSet != null && mainIndex != null) { + K[] ts = timeSet.toArray(); + int tsLength = ts.length; + for (int i = 0; i < tsLength; i++) { + int timestampIndex = timeSortedMap.get(ts[i]); + mainIndex.add(timestampIndex, element); + } } + } finally { + unlock(); } } public void clear(Element element) { - S timeSet = getTimeSet(element); - - if (timeSet != null && mainIndex != null) { - K[] ts = timeSet.toArray(); - int tsLength = ts.length; - for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - mainIndex.remove(timestampIndex, element); - } + lock(); + try { + S timeSet = getTimeSet(element); + + if (timeSet != null && mainIndex != null) { + K[] ts = timeSet.toArray(); + int tsLength = ts.length; + for (int i = 0; i < tsLength; i++) { + int timestampIndex = timeSortedMap.get(ts[i]); + mainIndex.remove(timestampIndex, element); + } - if (!viewIndexes.isEmpty()) { - for (Entry entry : viewIndexes.entrySet()) { - GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); - DirectedSubgraph graph = graphView.getDirectedGraph(); - boolean node = element instanceof Node; - if (node ? graph.contains((Node) element) : graph.contains((Edge) element)) { - for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - entry.getValue().remove(timestampIndex, element); + if (!viewIndexes.isEmpty()) { + for (Entry entry : viewIndexes.entrySet()) { + GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); + DirectedSubgraph graph = graphView.getDirectedGraph(); + boolean node = element instanceof Node; + if (node ? graph.contains((Node) element) : graph.contains((Edge) element)) { + for (int i = 0; i < tsLength; i++) { + int timestampIndex = timeSortedMap.get(ts[i]); + entry.getValue().remove(timestampIndex, element); + } } } } } - } - if (timeSet != null) { - remove(timeSet); - } + if (timeSet != null) { + remove(timeSet); + } - for (Object val : element.getAttributes()) { - if (val != null && val instanceof TimeMap) { - TimeMap dynamicValue = (TimeMap) val; - remove((M) dynamicValue); + synchronized (element) { + for (Object val : element.getAttributes()) { + if (val != null && val instanceof TimeMap) { + TimeMap dynamicValue = (TimeMap) val; + remove((M) dynamicValue); + } + } } + } finally { + unlock(); } } public void clear() { - timeSortedMap.clear(); - garbageQueue.clear(); - countMap = new int[0]; - length = 0; - - if (mainIndex != null) { - mainIndex.clear(); - - if (!viewIndexes.isEmpty()) { - for (TimeIndexImpl index : viewIndexes.values()) { - index.clear(); + lock(); + try { + timeSortedMap.clear(); + garbageQueue.clear(); + countMap = new int[0]; + length = 0; + + if (mainIndex != null) { + mainIndex.clear(); + + if (!viewIndexes.isEmpty()) { + for (TimeIndexImpl index : viewIndexes.values()) { + index.clear(); + } } } + } finally { + unlock(); } } @@ -264,13 +308,17 @@ public TimeIndex getIndex(Graph graph) { if (view.isMainView()) { return mainIndex; } - TimeIndexImpl viewIndex = viewIndexes.get(graph.getView()); - if (viewIndex == null) { - // TODO Make the auto-creation optional? - viewIndex = createViewIndex(graph); - viewIndexes.put(graph.getView(), viewIndex); + lock(); + try { + TimeIndexImpl viewIndex = viewIndexes.get(graph.getView()); + if (viewIndex == null) { + // TODO Make the auto-creation optional? + viewIndex = createViewIndex(graph); + } + return viewIndex; + } finally { + unlock(); } - return viewIndex; } protected TimeIndexImpl createViewIndex(Graph graph) { @@ -291,9 +339,14 @@ public void deleteViewIndex(Graph graph) { if (graph.getView().isMainView()) { throw new IllegalArgumentException("Can't delete a view index for the main view"); } - TimeIndexImpl index = viewIndexes.remove(graph.getView()); - if (index != null) { - index.clear(); + lock(); + try { + TimeIndexImpl index = viewIndexes.remove(graph.getView()); + if (index != null) { + index.clear(); + } + } finally { + unlock(); } } @@ -301,6 +354,7 @@ public void indexView(Graph graph) { TimeIndexImpl viewIndex = viewIndexes.get(graph.getView()); if (viewIndex != null) { graph.readLock(); + lock(); try { Iterator iterator = null; @@ -326,6 +380,7 @@ public void indexView(Graph graph) { } } finally { graph.readUnlock(); + unlock(); } } } @@ -333,30 +388,39 @@ public void indexView(Graph graph) { public void indexInView(T element, GraphView view) { TimeIndexImpl viewIndex = viewIndexes.get(view); if (viewIndex != null) { - S set = getTimeSet(element); - if (set != null) { - K[] ts = set.toArray(); - int tsLength = ts.length; - for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - viewIndex.add(timestampIndex, element); + lock(); + try { + S set = getTimeSet(element); + if (set != null) { + K[] ts = set.toArray(); + int tsLength = ts.length; + for (int i = 0; i < tsLength; i++) { + int timestampIndex = timeSortedMap.get(ts[i]); + viewIndex.add(timestampIndex, element); + } } + } finally { + unlock(); } } } public void clearInView(T element, GraphView view) { - ElementImpl elementImpl = (ElementImpl) element; TimeIndexImpl viewIndex = viewIndexes.get(view); if (viewIndex != null) { - S set = getTimeSet(element); - if (set != null) { - K[] ts = set.toArray(); - int tsLength = ts.length; - for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - viewIndex.remove(timestampIndex, elementImpl); + lock(); + try { + S set = getTimeSet(element); + if (set != null) { + K[] ts = set.toArray(); + int tsLength = ts.length; + for (int i = 0; i < tsLength; i++) { + int timestampIndex = timeSortedMap.get(ts[i]); + viewIndex.remove(timestampIndex, element); + } } + } finally { + unlock(); } } } @@ -429,4 +493,16 @@ public boolean deepEquals(TimeIndexStore obj) { } return true; } + + private void lock() { + if (lock != null) { + lock.lock(); + } + } + + private void unlock() { + if (lock != null) { + lock.unlock(); + } + } } diff --git a/src/main/java/org/gephi/graph/impl/TimeStore.java b/src/main/java/org/gephi/graph/impl/TimeStore.java index 22d8abe4..56c2c0c1 100644 --- a/src/main/java/org/gephi/graph/impl/TimeStore.java +++ b/src/main/java/org/gephi/graph/impl/TimeStore.java @@ -23,15 +23,15 @@ public class TimeStore { protected final GraphStore graphStore; - // Lock (optional - protected final GraphLockImpl lock; + // Lock (optional) + protected final TableLockImpl lock; // Store protected TimeIndexStore nodeIndexStore; protected TimeIndexStore edgeIndexStore; - public TimeStore(GraphStore store, GraphLockImpl graphLock, boolean indexed) { - lock = graphLock; - graphStore = store; + public TimeStore(GraphStore store, boolean indexed) { + this.graphStore = store; + this.lock = GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? new TableLockImpl() : null; TimeRepresentation timeRepresentation = GraphStoreConfiguration.DEFAULT_TIME_REPRESENTATION; if (store != null) { diff --git a/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java b/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java index 0ac2a2e0..c53cc70d 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java @@ -34,103 +34,117 @@ public TimestampIndexImpl(TimeIndexStore bi = sortedMap.double2IntEntrySet().iterator(); - while (bi.hasNext()) { - Double2IntMap.Entry entry = bi.next(); - double timestamp = entry.getDoubleKey(); - int index = entry.getIntValue(); - if (index < timestamps.length) { - TimeIndexEntry timestampEntry = timestamps[index]; - if (timestampEntry != null) { - return timestamp; + if (mainIndex) { + if (!sortedMap.isEmpty()) { + return sortedMap.firstDoubleKey(); + } + } else { + if (!sortedMap.isEmpty()) { + ObjectBidirectionalIterator bi = sortedMap.double2IntEntrySet().iterator(); + while (bi.hasNext()) { + Double2IntMap.Entry entry = bi.next(); + double timestamp = entry.getDoubleKey(); + int index = entry.getIntValue(); + if (index < timestamps.length) { + TimeIndexEntry timestampEntry = timestamps[index]; + if (timestampEntry != null) { + return timestamp; + } } } } } + return Double.NEGATIVE_INFINITY; + } finally { + unlock(); } - return Double.NEGATIVE_INFINITY; } @Override public double getMaxTimestamp() { - if (mainIndex) { + lock(); + try { Double2IntSortedMap sortedMap = (Double2IntSortedMap) timestampIndexStore.timeSortedMap; - if (!sortedMap.isEmpty()) { - return sortedMap.lastDoubleKey(); - } - } else { - Double2IntSortedMap sortedMap = (Double2IntSortedMap) timestampIndexStore.timeSortedMap; - if (!sortedMap.isEmpty()) { - ObjectBidirectionalIterator bi = sortedMap.double2IntEntrySet() - .iterator(sortedMap.double2IntEntrySet().last()); - while (bi.hasPrevious()) { - Double2IntMap.Entry entry = bi.previous(); - double timestamp = entry.getDoubleKey(); - int index = entry.getIntValue(); - if (index < timestamps.length) { - TimeIndexEntry timestampEntry = timestamps[index]; - if (timestampEntry != null) { - return timestamp; + if (mainIndex) { + if (!sortedMap.isEmpty()) { + return sortedMap.lastDoubleKey(); + } + } else { + if (!sortedMap.isEmpty()) { + ObjectBidirectionalIterator bi = sortedMap.double2IntEntrySet() + .iterator(sortedMap.double2IntEntrySet().last()); + while (bi.hasPrevious()) { + Double2IntMap.Entry entry = bi.previous(); + double timestamp = entry.getDoubleKey(); + int index = entry.getIntValue(); + if (index < timestamps.length) { + TimeIndexEntry timestampEntry = timestamps[index]; + if (timestampEntry != null) { + return timestamp; + } } } } } + return Double.POSITIVE_INFINITY; + } finally { + unlock(); } - return Double.POSITIVE_INFINITY; } @Override - public ElementIterable get(double timestamp) { + public ElementIterable get(double timestamp) { checkDouble(timestamp); - readLock(); - Integer index = timestampIndexStore.timeSortedMap.get(timestamp); - if (index != null && index < timestamps.length) { - TimeIndexEntry ts = timestamps[index]; - if (ts != null) { - return new ElementIterableImpl(new ElementIteratorImpl(ts.elementSet.iterator())); + lock(); + try { + Integer index = timestampIndexStore.timeSortedMap.get(timestamp); + if (index != null && index < timestamps.length) { + TimeIndexEntry ts = timestamps[index]; + if (ts != null) { + return new ElementSetWrapperIterable(new ObjectOpenHashSet<>(ts.elementSet)); + } } + return ElementIterable.EMPTY; + } finally { + unlock(); } - readUnlock(); - return ElementIterable.EMPTY; } @Override - public ElementIterable get(Interval interval) { + public ElementIterable get(Interval interval) { checkDouble(interval.getLow()); checkDouble(interval.getHigh()); - readLock(); - ObjectSet elements = new ObjectOpenHashSet<>(); - Double2IntSortedMap sortedMap = (Double2IntSortedMap) timestampIndexStore.timeSortedMap; - if (!sortedMap.isEmpty()) { - for (Double2IntMap.Entry entry : sortedMap.tailMap(interval.getLow()).double2IntEntrySet()) { - double timestamp = entry.getDoubleKey(); - int index = entry.getIntValue(); - if (timestamp <= interval.getHigh()) { - if (index < timestamps.length) { - TimeIndexEntry ts = timestamps[index]; - if (ts != null) { - elements.addAll(ts.elementSet); + lock(); + try { + ObjectSet elements = new ObjectOpenHashSet<>(); + Double2IntSortedMap sortedMap = (Double2IntSortedMap) timestampIndexStore.timeSortedMap; + if (!sortedMap.isEmpty()) { + for (Double2IntMap.Entry entry : sortedMap.tailMap(interval.getLow()).double2IntEntrySet()) { + double timestamp = entry.getDoubleKey(); + int index = entry.getIntValue(); + if (timestamp <= interval.getHigh()) { + if (index < timestamps.length) { + TimeIndexEntry ts = timestamps[index]; + if (ts != null) { + elements.addAll(ts.elementSet); + } } + } else { + break; } - } else { - break; } } + if (!elements.isEmpty()) { + return new ElementSetWrapperIterable(elements); + } + return ElementIterable.EMPTY; + } finally { + unlock(); } - if (!elements.isEmpty()) { - return new ElementIterableImpl(new ElementIteratorImpl(elements.iterator())); - } - readUnlock(); - return ElementIterable.EMPTY; } } diff --git a/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java b/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java index 8c5bddf1..55bd083e 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java @@ -22,7 +22,7 @@ public class TimestampIndexStore extends TimeIndexStore> { - public TimestampIndexStore(Class type, GraphLockImpl lock, boolean indexed) { + public TimestampIndexStore(Class type, TableLockImpl lock, boolean indexed) { super(type, lock, indexed, new Double2IntRBTreeMap()); mainIndex = indexed ? new TimestampIndexImpl(this, true) : null; } diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index 4acdfdeb..4a6d4f2a 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -30,10 +30,12 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnIterable; import org.gephi.graph.api.DirectedGraph; @@ -1111,6 +1113,11 @@ public Collection toCollection() { return Arrays.asList(idToNodeMap.values().toArray(new Node[0])); } + @Override + public Set toSet() { + return new HashSet<>(idToNodeMap.values()); + } + @Override public boolean add(Node node) { if (((BasicNode) node).getId() == null) { @@ -1574,7 +1581,7 @@ public Iterator iterator() { @Override public Node[] toArray() { List list = new ArrayList<>(); - for (; iterator.hasNext();) { + while (iterator.hasNext()) { list.add(iterator.next()); } return list.toArray(new Node[0]); @@ -1583,12 +1590,21 @@ public Node[] toArray() { @Override public Collection toCollection() { List list = new ArrayList<>(); - for (; iterator.hasNext();) { + while (iterator.hasNext()) { list.add(iterator.next()); } return list; } + @Override + public Set toSet() { + Set set = new HashSet<>(); + while (iterator.hasNext()) { + set.add(iterator.next()); + } + return set; + } + @Override public void doBreak() { // Not used because no locking @@ -1611,7 +1627,7 @@ public Iterator iterator() { @Override public Edge[] toArray() { List list = new ArrayList<>(); - for (; iterator.hasNext();) { + while (iterator.hasNext()) { list.add(iterator.next()); } return list.toArray(new Edge[0]); @@ -1620,12 +1636,21 @@ public Edge[] toArray() { @Override public Collection toCollection() { List list = new ArrayList<>(); - for (; iterator.hasNext();) { + while (iterator.hasNext()) { list.add(iterator.next()); } return list; } + @Override + public Set toSet() { + Set set = new HashSet<>(); + while (iterator.hasNext()) { + set.add(iterator.next()); + } + return set; + } + @Override public void doBreak() { // Not used because no locking diff --git a/src/test/java/org/gephi/graph/impl/EdgeImplTest.java b/src/test/java/org/gephi/graph/impl/EdgeImplTest.java index 2f5c62bc..84d7254b 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeImplTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeImplTest.java @@ -44,6 +44,14 @@ public void testSetGetWeight() { Assert.assertEquals(e.getWeight(), 42.0); } + @Test + public void testZeroWeight() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + Edge e = graphStore.getEdge("0"); + e.setWeight(0.0); + Assert.assertEquals(e.getWeight(), 0.0); + } + @Test public void testGetDefaultTimestampWeight() { Configuration config = new Configuration(); diff --git a/src/test/java/org/gephi/graph/impl/TimeStoreTest.java b/src/test/java/org/gephi/graph/impl/TimeStoreTest.java index 07d2abe9..ee7ad883 100644 --- a/src/test/java/org/gephi/graph/impl/TimeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/TimeStoreTest.java @@ -26,14 +26,14 @@ public class TimeStoreTest { @Test public void testEmpty() { - TimeStore store = new TimeStore(null, null, false); + TimeStore store = new TimeStore(null, false); Assert.assertTrue(store.isEmpty()); } @Test public void testDeepEqualsEmpty() { - TimeStore store1 = new TimeStore(null, null, false); - TimeStore store2 = new TimeStore(null, null, false); + TimeStore store1 = new TimeStore(null, false); + TimeStore store2 = new TimeStore(null, false); Assert.assertTrue(store1.deepEquals(store2)); Assert.assertEquals(store1.deepHashCode(), store2.deepHashCode()); @@ -42,21 +42,21 @@ public void testDeepEqualsEmpty() { @Test public void testGetMinNull() { GraphStore graphStore = new GraphStore(); - TimeStore store = new TimeStore(graphStore, null, true); + TimeStore store = new TimeStore(graphStore, true); Assert.assertEquals(store.getMin(graphStore), Double.NEGATIVE_INFINITY); } @Test public void testGetMaxNull() { GraphStore graphStore = new GraphStore(); - TimeStore store = new TimeStore(graphStore, null, true); + TimeStore store = new TimeStore(graphStore, true); Assert.assertEquals(store.getMax(graphStore), Double.POSITIVE_INFINITY); } @Test public void testGetMin() { GraphStore graphStore = new GraphStore(); - TimeStore store = new TimeStore(graphStore, null, true); + TimeStore store = new TimeStore(graphStore, true); store.nodeIndexStore.add(1.0); store.nodeIndexStore.add(2.0); Assert.assertEquals(store.getMin(graphStore), 1.0); @@ -65,7 +65,7 @@ public void testGetMin() { @Test public void testGetMax() { GraphStore graphStore = new GraphStore(); - TimeStore store = new TimeStore(graphStore, null, true); + TimeStore store = new TimeStore(graphStore, true); store.nodeIndexStore.add(1.0); store.nodeIndexStore.add(2.0); Assert.assertEquals(store.getMax(graphStore), 2.0); @@ -73,7 +73,7 @@ public void testGetMax() { @Test public void testClearEdges() { - TimeStore store = new TimeStore(null, null, true); + TimeStore store = new TimeStore(null, true); store.nodeIndexStore.add(1.0); store.edgeIndexStore.add(2.0); store.clearEdges(); @@ -84,7 +84,7 @@ public void testClearEdges() { @Test public void testClear() { - TimeStore store = new TimeStore(null, null, true); + TimeStore store = new TimeStore(null, true); store.nodeIndexStore.add(1.0); store.edgeIndexStore.add(2.0); store.clear(); @@ -95,8 +95,8 @@ public void testClear() { @Test public void testDeepEquals() { - TimeStore store1 = new TimeStore(null, null, true); - TimeStore store2 = new TimeStore(null, null, true); + TimeStore store1 = new TimeStore(null, true); + TimeStore store2 = new TimeStore(null, true); store1.nodeIndexStore.add(1.0); store1.edgeIndexStore.add(2.0); store2.nodeIndexStore.add(1.0); diff --git a/src/test/java/org/gephi/graph/impl/TimestampIndexImplTest.java b/src/test/java/org/gephi/graph/impl/TimestampIndexImplTest.java index 8dd1e0c3..86e58b31 100644 --- a/src/test/java/org/gephi/graph/impl/TimestampIndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/TimestampIndexImplTest.java @@ -29,7 +29,7 @@ public class TimestampIndexImplTest { @Test public void testGetMin() { - TimeStore timestampStore = new TimeStore(null, null, true); + TimeStore timestampStore = new TimeStore(null, true); TimestampIndexStore store = (TimestampIndexStore) timestampStore.nodeIndexStore; Assert.assertEquals(store.mainIndex.getMinTimestamp(), Double.NEGATIVE_INFINITY); @@ -70,7 +70,7 @@ public void testGetMinMaxWithView() { @Test public void testGetMax() { - TimeStore timestampStore = new TimeStore(null, null, true); + TimeStore timestampStore = new TimeStore(null, true); TimestampIndexStore store = (TimestampIndexStore) timestampStore.nodeIndexStore; Assert.assertEquals(store.mainIndex.getMaxTimestamp(), Double.POSITIVE_INFINITY); @@ -86,7 +86,7 @@ public void testGetMax() { @Test public void testGetElements() { - TimeStore timestampStore = new TimeStore(null, null, true); + TimeStore timestampStore = new TimeStore(null, true); TimestampIndexStore store = (TimestampIndexStore) timestampStore.nodeIndexStore; store.add(1.0); store.add(2.0); @@ -130,14 +130,14 @@ public void testGetElements() { @Test public void testHasNodesEdgesEmpty() { - TimeStore timestampStore = new TimeStore(null, null, true); + TimeStore timestampStore = new TimeStore(null, true); TimestampIndexStore store = (TimestampIndexStore) timestampStore.nodeIndexStore; Assert.assertFalse(store.mainIndex.hasElements()); } @Test public void testHasNodes() { - TimeStore timestampStore = new TimeStore(null, null, true); + TimeStore timestampStore = new TimeStore(null, true); TimestampIndexStore store = (TimestampIndexStore) timestampStore.nodeIndexStore; Assert.assertFalse(store.mainIndex.hasElements()); @@ -158,7 +158,7 @@ public void testHasNodes() { @Test public void testHasNodesClear() { - TimeStore timestampStore = new TimeStore(null, null, true); + TimeStore timestampStore = new TimeStore(null, true); TimestampIndexStore store = (TimestampIndexStore) timestampStore.nodeIndexStore; store.add(1.0); From 388f0646d5f782be38dcc4600f5ff1908b90a428 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 22 Jan 2022 15:55:08 +0100 Subject: [PATCH 050/271] Add new method isDynamicAttribute() to Column for utility --- src/main/java/org/gephi/graph/api/Column.java | 7 +++++++ src/main/java/org/gephi/graph/impl/ColumnImpl.java | 5 +++++ src/test/java/org/gephi/graph/impl/ColumnImplTest.java | 5 +++++ src/test/java/org/gephi/graph/impl/ColumnStoreTest.java | 5 +++++ 4 files changed, 22 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/Column.java b/src/main/java/org/gephi/graph/api/Column.java index 13f87fcd..1a7c995c 100644 --- a/src/main/java/org/gephi/graph/api/Column.java +++ b/src/main/java/org/gephi/graph/api/Column.java @@ -96,6 +96,13 @@ public interface Column { */ public boolean isDynamic(); + /** + * Returns true if this column is dynamic and has a TimeMap type. + * + * @return true if dynamic attribute type, false otherwise + */ + public boolean isDynamicAttribute(); + /** * Returns true if this column has a number type. * diff --git a/src/main/java/org/gephi/graph/impl/ColumnImpl.java b/src/main/java/org/gephi/graph/impl/ColumnImpl.java index 748e472d..5660c306 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnImpl.java @@ -125,6 +125,11 @@ public boolean isDynamic() { return dynamic; } + @Override + public boolean isDynamicAttribute() { + return dynamic && TimeMap.class.isAssignableFrom(typeClass); + } + @Override public boolean isReadOnly() { return readOnly; diff --git a/src/test/java/org/gephi/graph/impl/ColumnImplTest.java b/src/test/java/org/gephi/graph/impl/ColumnImplTest.java index aee2df15..f78715e9 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnImplTest.java @@ -55,18 +55,23 @@ public void testColumnStandardizedType() { public void testColumnIsDynamic() { ColumnImpl col1 = new ColumnImpl("0", String.class, null, null, Origin.DATA, false, false); Assert.assertFalse(col1.isDynamic()); + Assert.assertFalse(col1.isDynamicAttribute()); ColumnImpl col2 = new ColumnImpl("0", TimestampDoubleMap.class, null, null, Origin.DATA, false, false); Assert.assertTrue(col2.isDynamic()); + Assert.assertTrue(col2.isDynamicAttribute()); ColumnImpl col3 = new ColumnImpl("0", IntervalDoubleMap.class, null, null, Origin.DATA, false, false); Assert.assertTrue(col3.isDynamic()); + Assert.assertTrue(col3.isDynamicAttribute()); ColumnImpl col4 = new ColumnImpl("0", IntervalSet.class, null, null, Origin.DATA, false, false); Assert.assertTrue(col4.isDynamic()); + Assert.assertFalse(col4.isDynamicAttribute()); ColumnImpl col5 = new ColumnImpl("0", TimestampSet.class, null, null, Origin.DATA, false, false); Assert.assertTrue(col5.isDynamic()); + Assert.assertFalse(col5.isDynamicAttribute()); } @Test diff --git a/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java b/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java index 35d1cddb..5c5d144e 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java @@ -112,6 +112,11 @@ public boolean isDynamic() { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public boolean isDynamicAttribute() { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public boolean isNumber() { throw new UnsupportedOperationException("Not supported yet."); From 487599f436dc375d56fe60c7fb43e60d60d05872 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 22 Jan 2022 16:51:20 +0100 Subject: [PATCH 051/271] Fix bug #146 in GraphBridge.copyNodes --- .../org/gephi/graph/impl/GraphBridgeImpl.java | 12 ++++++++ .../org/gephi/graph/impl/GraphBridgeTest.java | 30 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java index f098535f..c59887ad 100644 --- a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java @@ -97,6 +97,9 @@ public void copyNodes(Node[] nodes) { if (store.getNode(node.getId()) == null) { Node nodeCopy = factory.newNode(node.getId()); + // Time set + copyTimeSet(node, nodeCopy); + // Properties copyNodeProperties(node, nodeCopy); @@ -119,6 +122,9 @@ public void copyNodes(Node[] nodes) { Edge edgeCopy = factory.newEdge(edge.getId(), source, target, edge.getType(), 0.0, edge.isDirected()); + // Time set + copyTimeSet(edge, edgeCopy); + // Weight copyEdgeWeight(edge, edgeCopy); @@ -173,6 +179,12 @@ private void copyTextProperties(TextProperties text, TextProperties textCopy) { textCopy.setVisible(text.isVisible()); } + private void copyTimeSet(Element element, Element elementCopy) { + Column sourceColumn = element.getTable().getColumn(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); + Column destColumn = elementCopy.getTable().getColumn(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); + elementCopy.setAttribute(destColumn, element.getAttribute(sourceColumn)); + } + private void copyColumns(TableImpl sourceTable, TableImpl destTable) { for (Column col : sourceTable.toArray()) { if (!col.isProperty() && !destTable.hasColumn(col.getId())) { diff --git a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java index 8f5b08e5..fffd719e 100644 --- a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java @@ -330,4 +330,34 @@ public void testCopyEdgeAttributes() { Assert.assertEquals(e0Copy.getAttribute(c2Copy, 1.0), 10); Assert.assertEquals(e0Copy.getAttribute(c2Copy, 2.0), 20); } + + @Test + public void testCopyTimestampSet() { + GraphStore source = GraphGenerator.generateTinyGraphStore(); + Node n1 = source.getNode("1"); + n1.addTimestamp(42.0); + + GraphModelImpl model = new GraphModelImpl(); + GraphStore dest = model.store; + new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); + + Node n1Copy = dest.getNode("1"); + Assert.assertTrue(n1Copy.hasTimestamp(42.0)); + } + + @Test + public void testCopyIntervalSet() { + GraphStore source = GraphGenerator.generateTinyGraphStore(TimeRepresentation.INTERVAL); + Node n1 = source.getNode("1"); + n1.addInterval(new Interval(1.0, 2.0)); + + Configuration config = new Configuration(); + config.setTimeRepresentation(TimeRepresentation.INTERVAL); + GraphModelImpl model = new GraphModelImpl(config); + GraphStore dest = model.store; + new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); + + Node n1Copy = dest.getNode("1"); + Assert.assertTrue(n1Copy.hasInterval(new Interval(1.0, 2.0))); + } } From 92df2cef2092d2a80b324a2bd8f9589473169e3a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 22 Jan 2022 17:57:30 +0100 Subject: [PATCH 052/271] Fix bug #147 on edge removal for multi-graph --- .../java/org/gephi/graph/impl/EdgeStore.java | 4 ++-- .../org/gephi/graph/impl/EdgeStoreTest.java | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 3a2cb7d9..6890adff 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -244,7 +244,7 @@ private void removeOutEdge(EdgeImpl edge) { EdgeImpl[] headOutArray = source.headOut; headOutArray[type] = nextOutEdge; if (nextOutEdge == null && type > GraphStoreConfiguration.EDGESTORE_DEFAULT_TYPE_COUNT - 1 && type == headOutArray.length - 1) { - trimHeadOutCapacity(source, type - 1); + trimHeadOutCapacity(source, headOutArray.length - 1); } } else { EdgeImpl previousOutEdge = get(previousOutEdgeId); @@ -271,7 +271,7 @@ private void removeInEdge(EdgeImpl edge) { EdgeImpl[] headInArray = target.headIn; headInArray[type] = nextInEdge; if (nextInEdge == null && type > GraphStoreConfiguration.EDGESTORE_DEFAULT_TYPE_COUNT - 1 && type == headInArray.length - 1) { - trimHeadInCapacity(target, type - 1); + trimHeadInCapacity(target, headInArray.length - 1); } } else { EdgeImpl previousInEdge = get(previousInEdgeId); diff --git a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java index 2dc5fc31..488bb60d 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java @@ -646,6 +646,24 @@ public void testRemoveMultitypes() { Assert.assertEquals(edgeStore.size(1), 0); } + @Test + public void testRemoveMultitypesReverseOrder() { + NodeStore nodeStore = new NodeStore(); + NodeImpl n1 = new NodeImpl("0"); + NodeImpl n2 = new NodeImpl("1"); + nodeStore.add(n1); + nodeStore.add(n2); + + EdgeImpl e1 = new EdgeImpl("0", n1, n2, 0, 1.0, true); + EdgeImpl e2 = new EdgeImpl("1", n1, n2, 1, 1.0, true); + EdgeStore edgeStore = new EdgeStore(); + edgeStore.add(e1); + edgeStore.add(e2); + + edgeStore.remove(e2); + edgeStore.remove(e1); + } + @Test public void testOutIterator() { EdgeImpl[] edges = GraphGenerator.generateSmallEdgeList(); From c836b1bb4d8a863a142b93933dc84580916e8b8e Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 22 Jan 2022 20:33:31 +0100 Subject: [PATCH 053/271] Release 0.6.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7bc24111..4da2e0d9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.1-SNAPSHOT + 0.6.1 jar GraphStore From b168a2f7952e778312e2c5b62a542fcabd9b8d34 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 22 Jan 2022 20:43:23 +0100 Subject: [PATCH 054/271] Set snapshot version 0.6.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4da2e0d9..ed58f55a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.1 + 0.6.2-SNAPSHOT jar GraphStore From e19d4f4ba69d23d9ec349dd0367fb4baf31c87e9 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 29 Jan 2022 13:28:59 +0100 Subject: [PATCH 055/271] Add getEdgeTypeLabels(boolean includeEmpty) to GraphModel --- .../java/org/gephi/graph/api/GraphModel.java | 9 +++++++ .../org/gephi/graph/impl/GraphModelImpl.java | 15 +++++++++++ .../org/gephi/graph/impl/GraphModelTest.java | 26 +++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index 4d25d5a1..5ed138f5 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -314,6 +314,15 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce */ public Object[] getEdgeTypeLabels(); + /** + * Returns the edge type labels. + * + * @param includeEmpty true to include labels without edges + * + * @return edge type labels + */ + public Object[] getEdgeTypeLabels(boolean includeEmpty); + /** * Returns true if the graph is directed. * diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 0a0e77dd..ce97bb48 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -15,6 +15,7 @@ */ package org.gephi.graph.impl; +import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Element; @@ -186,6 +187,20 @@ public Object[] getEdgeTypeLabels() { } } + @Override + public Object[] getEdgeTypeLabels(boolean includeEmpty) { + if (includeEmpty) { + return getEdgeTypeLabels(); + } + store.autoReadLock(); + try { + return Arrays.stream(store.edgeTypeStore.getLabels()) + .filter(l -> store.getEdgeCount(store.edgeTypeStore.getId(l)) > 0).toArray(); + } finally { + store.autoReadUnlock(); + } + } + @Override public int[] getEdgeTypes() { store.autoReadLock(); diff --git a/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/src/test/java/org/gephi/graph/impl/GraphModelTest.java index 9cee9e0f..35e7947f 100644 --- a/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -116,6 +116,32 @@ public void testGetEdgeTypeLabels() { GraphModelImpl graphModel = new GraphModelImpl(); graphModel.addEdgeType("foo"); Assert.assertEquals(graphModel.getEdgeTypeLabels(), new Object[] { null, "foo" }); + Assert.assertEquals(graphModel.getEdgeTypeLabels(true), new Object[] { null, "foo" }); + } + + @Test + public void testGetEdgeTypeLabelsEmpty() { + GraphModelImpl graphModel = new GraphModelImpl(); + graphModel.addEdgeType("foo"); + Assert.assertEquals(graphModel.getEdgeTypeLabels(false), new Object[] {}); + } + + @Test + public void testGetEdgeTypeLabelsNotEmpty() { + GraphModelImpl graphModel = GraphGenerator.generateTinyGraphStore().graphModel; + Assert.assertEquals(graphModel.getEdgeTypeLabels(false), new Object[] { null }); + } + + @Test + public void testGetEdgeTypeLabelsNotEmptyMultiGraph() { + GraphModelImpl graphModel = GraphGenerator.generateTinyGraphStore().graphModel; + Node n1 = graphModel.store.getNode("1"); + Node n2 = graphModel.store.getNode("2"); + graphModel.addEdgeType("bar"); + int type = graphModel.addEdgeType("foo"); + Edge e1 = graphModel.store.factory.newEdge("1", n1, n2, type, 1.0, false); + graphModel.store.addEdge(e1); + Assert.assertEquals(graphModel.getEdgeTypeLabels(false), new Object[] { null, "foo" }); } @Test From 0be767a377beb08e6269d791daeb681912047ac6 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 29 Jan 2022 13:58:00 +0100 Subject: [PATCH 056/271] Add additional test about element property indicies --- .../org/gephi/graph/impl/IndexStoreTest.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java index a423c2ba..ec356790 100644 --- a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; import org.gephi.graph.api.Column; +import org.gephi.graph.api.Edge; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphView; @@ -37,6 +38,15 @@ public void testEmpty() { Assert.assertEquals(mainIndex.size(), GraphStoreConfiguration.NODE_DEFAULT_COLUMNS); } + @Test + public void testEmptyForEdge() { + GraphStore graphStore = new GraphStore(); + ColumnStore columnStore = graphStore.edgeTable.store; + IndexStore indexStore = columnStore.indexStore; + IndexImpl mainIndex = indexStore.mainIndex; + Assert.assertEquals(mainIndex.size(), GraphStoreConfiguration.EDGE_DEFAULT_COLUMNS); + } + @Test public void testAddColumn() { ColumnStore store = generateEmptyNodeStore(); @@ -242,6 +252,54 @@ public void testClear() { Assert.assertTrue(mainIndex.values(col).isEmpty()); } + @Test + public void testNodePropertyIndices() { + GraphStore graphStore = new GraphStore(); + ColumnStore columnStore = graphStore.nodeTable.store; + IndexImpl mainIndex = columnStore.indexStore.mainIndex; + + Column idCol = columnStore.getColumnByIndex(GraphStoreConfiguration.ELEMENT_ID_INDEX); + Column labelCol = columnStore.getColumnByIndex(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); + + ColumnIndexImpl idIndex = mainIndex.getIndex(idCol); + ColumnIndexImpl labelIndex = mainIndex.getIndex(labelCol); + + Assert.assertNotNull(idIndex); + Assert.assertNotNull(labelIndex); + + NodeImpl n1 = new NodeImpl("0"); + graphStore.addNode(n1); + Assert.assertEquals(mainIndex.count(idCol, "0"), 1); + + n1.setLabel("foo"); + Assert.assertEquals(mainIndex.count(labelCol, "foo"), 1); + } + + @Test + public void testEdgePropertyIndices() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + ColumnStore columnStore = graphStore.edgeTable.store; + IndexImpl mainIndex = columnStore.indexStore.mainIndex; + + Column idCol = columnStore.getColumnByIndex(GraphStoreConfiguration.ELEMENT_ID_INDEX); + Column labelCol = columnStore.getColumnByIndex(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); + Column weigthCol = columnStore.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + + ColumnIndexImpl idIndex = mainIndex.getIndex(idCol); + ColumnIndexImpl labelIndex = mainIndex.getIndex(labelCol); + ColumnIndexImpl weightIndex = mainIndex.getIndex(weigthCol); + + Assert.assertNotNull(idIndex); + Assert.assertNotNull(labelIndex); + Assert.assertNotNull(weightIndex); + + Assert.assertEquals(mainIndex.count(idCol, "0"), 1); + Edge e0 = graphStore.getEdge("0"); + e0.setLabel("foo"); + Assert.assertEquals(mainIndex.count(labelCol, "foo"), 1); + Assert.assertEquals(mainIndex.count(weigthCol, 1.0), 1); + } + @Test public void testCreateViewIndex() { GraphStore graphStore = generateBasicGraphStoreWithColumns(); From d7f93f322935e165bf2676443d3da001cb40af71 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 5 Feb 2022 12:58:09 +0100 Subject: [PATCH 057/271] Add Column.exists() as new utility --- src/main/java/org/gephi/graph/api/Column.java | 7 +++++++ src/main/java/org/gephi/graph/impl/ColumnImpl.java | 5 +++++ src/test/java/org/gephi/graph/impl/ColumnImplTest.java | 6 ++++++ src/test/java/org/gephi/graph/impl/ColumnStoreTest.java | 5 +++++ src/test/java/org/gephi/graph/impl/TableImplTest.java | 1 + 5 files changed, 24 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/Column.java b/src/main/java/org/gephi/graph/api/Column.java index 1a7c995c..c7d9b9c1 100644 --- a/src/main/java/org/gephi/graph/api/Column.java +++ b/src/main/java/org/gephi/graph/api/Column.java @@ -110,6 +110,13 @@ public interface Column { */ public boolean isNumber(); + /** + * Returns true if this column exists and belong to a table. + * + * @return true if exists, false otherwise + */ + public boolean exists(); + /** * Returns true if this column is a property. *

diff --git a/src/main/java/org/gephi/graph/impl/ColumnImpl.java b/src/main/java/org/gephi/graph/impl/ColumnImpl.java index 5660c306..8513a277 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnImpl.java @@ -153,6 +153,11 @@ public void setStoreId(int storeId) { this.storeId = storeId; } + @Override + public boolean exists() { + return storeId != ColumnStore.NULL_ID; + } + @Override public String toString() { return title + " (" + typeClass.toString() + ")"; diff --git a/src/test/java/org/gephi/graph/impl/ColumnImplTest.java b/src/test/java/org/gephi/graph/impl/ColumnImplTest.java index f78715e9..549285e4 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnImplTest.java @@ -153,4 +153,10 @@ public void testColumnDeepHashcode() { col2.setEstimator(Estimator.MIN); Assert.assertNotEquals(col1.deepHashCode(), col2.deepHashCode()); } + + @Test + public void testColumnDoesNotExist() { + ColumnImpl col1 = new ColumnImpl("0", int.class, null, null, Origin.DATA, false, false); + Assert.assertFalse(col1.exists()); + } } diff --git a/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java b/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java index 5c5d144e..96d399b9 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java @@ -151,6 +151,11 @@ public Estimator getEstimator() { public void setEstimator(Estimator estimator) { throw new UnsupportedOperationException("Not supported yet."); } + + @Override + public boolean exists() { + throw new UnsupportedOperationException("Not supported yet."); + } }); } diff --git a/src/test/java/org/gephi/graph/impl/TableImplTest.java b/src/test/java/org/gephi/graph/impl/TableImplTest.java index cc52e43c..49c647ae 100644 --- a/src/test/java/org/gephi/graph/impl/TableImplTest.java +++ b/src/test/java/org/gephi/graph/impl/TableImplTest.java @@ -41,6 +41,7 @@ public void testAddColumnDefault() { Assert.assertEquals(table.countColumns(), 1); Assert.assertEquals(table.getColumn("Id"), col); Assert.assertEquals(table.getColumn("id"), col); + Assert.assertTrue(col.exists()); } @Test From 327f0e0e05e91af16c8b99fd638b3552432a2663 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 12 Feb 2022 13:26:26 +0100 Subject: [PATCH 058/271] Add min/max to TimeSet and Element.getTimeBounds --- .../java/org/gephi/graph/api/Element.java | 10 ++++++ .../gephi/graph/api/types/IntervalSet.java | 32 +++++++++++++++++++ .../org/gephi/graph/api/types/TimeSet.java | 30 ++++++++++++++++- .../gephi/graph/api/types/TimestampSet.java | 26 +++++++++++++++ .../org/gephi/graph/impl/ElementImpl.java | 13 ++++++++ .../graph/api/types/IntervalSetTest.java | 28 +++++++++++++++- .../graph/api/types/TimestampSetTest.java | 29 +++++++++++++++++ .../org/gephi/graph/impl/BasicGraphStore.java | 5 +++ .../org/gephi/graph/impl/GraphStoreTest.java | 5 +++ 9 files changed, 176 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/Element.java b/src/main/java/org/gephi/graph/api/Element.java index 6a347997..bdd7a9c5 100644 --- a/src/main/java/org/gephi/graph/api/Element.java +++ b/src/main/java/org/gephi/graph/api/Element.java @@ -319,6 +319,16 @@ public interface Element extends ElementProperties { */ public Interval[] getIntervals(); + /** + * Gets the time bounds. + *

+ * The time bounds is an interval made of the minimum and maximum time observed + * in this element. + * + * @return time bounds + */ + public Interval getTimeBounds(); + /** * Clears all attribute values. */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalSet.java b/src/main/java/org/gephi/graph/api/types/IntervalSet.java index 5d980ddc..d886869a 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalSet.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalSet.java @@ -85,6 +85,38 @@ public boolean isEmpty() { return size == 0; } + @Override + public Interval getMax() { + if (size > 0) { + return new Interval(array[array.length - 2], array[array.length - 1]); + } + return null; + } + + @Override + public Interval getMin() { + if (size > 0) { + return new Interval(array[0], array[1]); + } + return null; + } + + @Override + public Double getMaxDouble() { + if (size > 0) { + return array[array.length - 1]; + } + return null; + } + + @Override + public Double getMinDouble() { + if (size > 0) { + return array[0]; + } + return null; + } + /** * Returns true if this set contains an interval that starts or ends at * timestamp. diff --git a/src/main/java/org/gephi/graph/api/types/TimeSet.java b/src/main/java/org/gephi/graph/api/types/TimeSet.java index 14368697..f1653751 100644 --- a/src/main/java/org/gephi/graph/api/types/TimeSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimeSet.java @@ -64,6 +64,34 @@ public interface TimeSet { */ public boolean contains(K key); + /** + * Returns the minimum key in the set + * + * @return minimum key, or null if the set is empty. + */ + public K getMin(); + + /** + * Returns the maximum key in the set + * + * @return maximum key, or null if the set is empty. + */ + public K getMax(); + + /** + * Returns the minimum timestamp in the set + * + * @return minimum timestamp, or null if the set is empty. + */ + public Double getMinDouble(); + + /** + * Returns the maximum timestamp in the set + * + * @return maximum timestamp, or null if the set is empty. + */ + public Double getMaxDouble(); + /** * Returns an array of all keys in this set. *

@@ -76,7 +104,7 @@ public interface TimeSet { /** * Returns the same result as {@link #toArray() } but in a primitive array if - * the underlying storage is in a primtive form. + * the underlying storage is in a primitive form. * * @return array of all keys */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampSet.java b/src/main/java/org/gephi/graph/api/types/TimestampSet.java index f700379e..a0e36092 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampSet.java @@ -84,6 +84,32 @@ public boolean isEmpty() { return size == 0; } + @Override + public Double getMax() { + if (size > 0) { + return array[array.length - 1]; + } + return null; + } + + @Override + public Double getMin() { + if (size > 0) { + return array[0]; + } + return null; + } + + @Override + public Double getMaxDouble() { + return getMax(); + } + + @Override + public Double getMinDouble() { + return getMin(); + } + @Override public boolean contains(Double timestamp) { int index = Arrays.binarySearch(array, timestamp); diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index 4a41a02c..7cfc8511 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -551,6 +551,19 @@ public Interval[] getIntervals() { return (Interval[]) res; } + @Override + public Interval getTimeBounds() { + TimeSet timeSet = getTimeSet(); + if (timeSet != null) { + Double min = timeSet.getMinDouble(); + Double max = timeSet.getMaxDouble(); + if (min != null) { + return new Interval(min, max); + } + } + return null; + } + private Object getTimeSetArray() { checkEnabledTimeSet(); diff --git a/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java b/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java index be0e0b6b..9c0f080b 100644 --- a/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java +++ b/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java @@ -93,7 +93,7 @@ public void testDuplicate() { } @Test - public void testContinous() { + public void testContinuous() { IntervalSet set = new IntervalSet(); Assert.assertTrue(set.add(new Interval(0.0, 1.0))); Assert.assertTrue(set.add(new Interval(1.0, 2.0))); @@ -109,6 +109,32 @@ public void testContinous() { Assert.assertTrue(set.contains(new Interval(1.0, 1.0))); } + @Test + public void testSorted() { + IntervalSet set = new IntervalSet(); + set.add(new Interval(4.0, 5.0)); + set.add(new Interval(1.0, 4.0)); + + Assert.assertEquals(1.0, set.toArray()[0].getLow()); + Assert.assertEquals(5.0, set.toArray()[1].getHigh()); + } + + @Test + public void testMinMax() { + IntervalSet set = new IntervalSet(); + Assert.assertNull(set.getMax()); + Assert.assertNull(set.getMin()); + Assert.assertNull(set.getMaxDouble()); + Assert.assertNull(set.getMinDouble()); + + set.add(new Interval(4.0, 5.0)); + set.add(new Interval(1.0, 4.0)); + Assert.assertEquals(set.getMin().getLow(), 1.0); + Assert.assertEquals(set.getMax().getHigh(), 5.0); + Assert.assertEquals(set.getMinDouble(), 1.0); + Assert.assertEquals(set.getMaxDouble(), 5.0); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testStartOverlappingAbove() { IntervalSet set = new IntervalSet(); diff --git a/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java b/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java index b10a1f35..bcf2729a 100644 --- a/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java +++ b/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java @@ -19,6 +19,7 @@ import it.unimi.dsi.fastutil.doubles.DoubleSet; import java.util.Random; import org.gephi.graph.api.AttributeUtils; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.impl.NumberGenerator; import org.joda.time.DateTimeZone; @@ -169,6 +170,34 @@ public void testRemoveAddLoop() { .sortAndRemoveDuplicates(doubleSet.toDoubleArray())); } + @Test + public void testSorted() { + TimestampSet set = new TimestampSet(); + set.add(3.0); + set.add(1.0); + set.add(2.0); + + testDoubleArrayEquals(set.toPrimitiveArray(), new double[] { 1.0, 2.0, 3.0 }); + set.remove(1.0); + testDoubleArrayEquals(set.toPrimitiveArray(), new double[] { 2.0, 3.0 }); + } + + @Test + public void testMinMax() { + TimestampSet set = new TimestampSet(); + Assert.assertNull(set.getMax()); + Assert.assertNull(set.getMin()); + Assert.assertNull(set.getMaxDouble()); + Assert.assertNull(set.getMinDouble()); + + set.add(3.0); + set.add(1.0); + Assert.assertEquals(set.getMin(), 1.0); + Assert.assertEquals(set.getMax(), 3.0); + Assert.assertEquals(set.getMinDouble(), 1.0); + Assert.assertEquals(set.getMaxDouble(), 3.0); + } + @Test public void testClear() { TimestampSet set = new TimestampSet(); diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index 4a6d4f2a..fa60b6b3 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -695,6 +695,11 @@ public Interval[] getIntervals() { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public Interval getTimeBounds() { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public void setAttribute(Column column, Object value, Interval interval) { throw new UnsupportedOperationException("Not supported yet."); diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index 501e0b9b..f5c00f17 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -448,6 +448,11 @@ public Interval[] getIntervals() { throw new UnsupportedOperationException("Not supported yet."); } + @Override + public Interval getTimeBounds() { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public Iterable getAttributes(Column column) { throw new UnsupportedOperationException("Not supported yet."); From 977af7956c11e4a451447cbc5d353ef5976eb93c Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 12 Feb 2022 13:54:32 +0100 Subject: [PATCH 059/271] Add version to Index --- src/main/java/org/gephi/graph/api/ColumnIndex.java | 8 ++++++++ .../org/gephi/graph/impl/ColumnNoIndexImpl.java | 13 +++++++++++++ .../gephi/graph/impl/ColumnStandardIndexImpl.java | 14 ++++++++++++++ .../org/gephi/graph/impl/ColumnNoIndexTest.java | 9 +++++++++ .../gephi/graph/impl/ColumnStandardIndexTest.java | 11 +++++++++++ 5 files changed, 55 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/ColumnIndex.java b/src/main/java/org/gephi/graph/api/ColumnIndex.java index 3c0e4088..32c28d99 100644 --- a/src/main/java/org/gephi/graph/api/ColumnIndex.java +++ b/src/main/java/org/gephi/graph/api/ColumnIndex.java @@ -99,4 +99,12 @@ public interface ColumnIndex extends Iterable implements ColumnIndexImpl< // Graph protected final Graph graph; protected final GraphLock graphLock; + // Version + protected final AtomicInteger version = new AtomicInteger(Integer.MIN_VALUE); protected ColumnNoIndexImpl(ColumnImpl column, Graph graph, Class elementClass) { this.column = column; @@ -183,6 +186,11 @@ public Column getColumn() { return column; } + @Override + public int getVersion() { + return version.get(); + } + @Override public Iterator>> iterator() { // TODO @@ -192,26 +200,31 @@ public Column getColumn() { @Override public void clear() { // Nothing to clear + version.incrementAndGet(); } @Override public void destroy() { // Nothing to destroy + version.incrementAndGet(); } @Override public K putValue(T element, K value) { + version.incrementAndGet(); return value; } @Override public K replaceValue(T element, K oldValue, K newValue) { + version.incrementAndGet(); return newValue; } @Override public void removeValue(T element, K value) { // Nothing to remove + version.incrementAndGet(); } private class ElementWithValueIterable implements Iterable { diff --git a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java index 033d5569..758579d8 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java @@ -42,6 +42,7 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; +import java.util.concurrent.atomic.AtomicInteger; import org.gephi.graph.api.Element; public abstract class ColumnStandardIndexImpl implements ColumnIndexImpl { @@ -54,6 +55,8 @@ public abstract class ColumnStandardIndexImpl implements C protected Map> map; // Variable protected int elements; + // Version + protected final AtomicInteger version = new AtomicInteger(Integer.MIN_VALUE); protected ColumnStandardIndexImpl(ColumnImpl column) { this.column = column; @@ -68,6 +71,7 @@ public K putValue(T element, K value) { if (value == null) { if (nullSet.add(element)) { elements++; + version.incrementAndGet(); } } else { ValueSet set = getValueSet(value); @@ -78,6 +82,7 @@ public K putValue(T element, K value) { if (set.add(element)) { elements++; + version.incrementAndGet(); } } } finally { @@ -93,11 +98,13 @@ public void removeValue(T element, K value) { if (value == null) { if (nullSet.remove(element)) { elements--; + version.incrementAndGet(); } } else { ValueSet set = getValueSet(value); if (set.remove(element)) { elements--; + version.incrementAndGet(); } if (set.isEmpty()) { removeValue(value); @@ -200,6 +207,7 @@ public void destroy() { map = null; nullSet.clear(); elements = 0; + version.incrementAndGet(); unlock(); } @@ -209,6 +217,7 @@ public void clear() { map.clear(); nullSet.clear(); elements = 0; + version.incrementAndGet(); unlock(); } @@ -254,6 +263,11 @@ public ColumnImpl getColumn() { return column; } + @Override + public int getVersion() { + return version.get(); + } + void lock() { if (lock != null) { lock.lock(); diff --git a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java index 72cdb954..1ca299b1 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java @@ -177,6 +177,15 @@ public void testGetMinValueNotSortable() { fooIndex.getMinValue(); } + @Test + public void testVersion() { + int version = fooIndex.getVersion(); + Node node = graphStore.factory.newNode("1"); + fooIndex.putValue(node, "bar"); + + Assert.assertTrue(fooIndex.getVersion() > version); + } + private ColumnNoIndexImpl createIndex(GraphStore graphStore, String id) { return new ColumnNoIndexImpl(graphStore.nodeTable.getColumn(id), graphStore, Node.class); } diff --git a/src/test/java/org/gephi/graph/impl/ColumnStandardIndexTest.java b/src/test/java/org/gephi/graph/impl/ColumnStandardIndexTest.java index be430498..69877161 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnStandardIndexTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnStandardIndexTest.java @@ -577,6 +577,17 @@ public void testArrayTypes() { } } + @Test + public void testVersion() { + IndexImpl index = generateEmptyIndex(); + Column column = index.columnStore.getColumn("age"); + int version = index.getColumnIndex(column).getVersion(); + + NodeImpl n = new NodeImpl(0); + index.put(column, 12, n); + Assert.assertTrue(index.getColumnIndex(column).getVersion() > version); + } + // UTILITIES private NodeImpl[] generateNodesWithUniqueAttributes(IndexImpl index, boolean withNulls) { int count = 100; From cbd70ae231c8edcbd8eb1ae0a47bb77b3bc52575 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 12 Feb 2022 13:57:54 +0100 Subject: [PATCH 060/271] Set version to 0.6.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ed58f55a..6106e17f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.2-SNAPSHOT + 0.6.2 jar GraphStore From 2c6d5848d9df40c7f7c367ec8669e0cb3a1f4a85 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 12 Feb 2022 14:21:23 +0100 Subject: [PATCH 061/271] Set version to 0.6.3-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6106e17f..04dceb17 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.2 + 0.6.3-SNAPSHOT jar GraphStore From 84d54202f351dd092278c60efed48b9410801de3 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 14 Feb 2022 21:08:27 +0100 Subject: [PATCH 062/271] Add support for dynamic types in ColumnNoIndex --- .../gephi/graph/impl/ColumnNoIndexImpl.java | 13 +++--- .../graph/impl/ColumnStandardIndexImpl.java | 4 ++ .../java/org/gephi/graph/impl/IndexImpl.java | 3 +- .../gephi/graph/impl/ColumnNoIndexTest.java | 40 +++++++++++++++++++ .../org/gephi/graph/impl/TableImplTest.java | 5 +++ 5 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java index 471d0e5c..2a23e6e0 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; @@ -66,7 +67,7 @@ public int count(K value) { if (elementIterator != null) { while (elementIterator.hasNext()) { ElementImpl element = (ElementImpl) elementIterator.next(); - K obj = (K) element.getAttribute(column); + K obj = (K) element.getAttribute(column, graph.getView()); if (value == null && obj == null) { count++; } else if (value != null && value.equals(obj)) { @@ -94,7 +95,7 @@ public Collection values() { if (elementIterator != null) { while (elementIterator.hasNext()) { ElementImpl element = (ElementImpl) elementIterator.next(); - K obj = (K) element.getAttribute(column); + K obj = (K) element.getAttribute(column, graph.getView()); set.add(obj); } } @@ -121,7 +122,7 @@ public int countElements() { @Override public boolean isSortable() { - return Number.class.isAssignableFrom(column.getTypeClass()); + return AttributeUtils.isNumberType(column.getTypeClass()) && !AttributeUtils.isArrayType(column.getTypeClass()); } @Override @@ -137,7 +138,7 @@ public Number getMinValue() { double minN = Double.POSITIVE_INFINITY; while (elementIterator.hasNext()) { ElementImpl element = (ElementImpl) elementIterator.next(); - Number num = (Number) element.getAttribute(column); + Number num = (Number) element.getAttribute(column, graph.getView()); if (min == null || (num != null && num.doubleValue() < minN)) { if (num != null) { minN = num.doubleValue(); @@ -166,7 +167,7 @@ public Number getMaxValue() { while (elementIterator.hasNext()) { ElementImpl element = (ElementImpl) elementIterator.next(); - Number num = (Number) element.getAttribute(column); + Number num = (Number) element.getAttribute(column, graph.getView()); if (max == null || (num != null && num.doubleValue() > maxN)) { if (num != null) { maxN = num.doubleValue(); @@ -259,7 +260,7 @@ public ElementWithValueIterator(Iterator itr, K value) { public boolean hasNext() { while (pointer == null && itr.hasNext()) { T element = itr.next(); - K val = (K) element.getAttribute(column); + K val = (K) element.getAttribute(column, graph.getView()); if ((value == null && val == null) || (val != null && val.equals(value))) { pointer = element; } diff --git a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java index 758579d8..566489b7 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java @@ -64,6 +64,10 @@ protected ColumnStandardIndexImpl(ColumnImpl column) { this.lock = GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? new TableLockImpl() : null; } + protected static boolean isSupportedType(ColumnImpl col) { + return !col.isDynamicAttribute(); + } + @Override public K putValue(T element, K value) { lock(); diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index bcac3cff..a6cd4224 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -272,7 +272,8 @@ protected int size() { } ColumnIndexImpl createIndex(ColumnImpl col) { - return col.isIndexed() ? createStandardIndex(col) : createNoIndex(col, graph); + return col.isIndexed() && ColumnStandardIndexImpl.isSupportedType(col) ? createStandardIndex(col) + : createNoIndex(col, graph); } ColumnNoIndexImpl createNoIndex(ColumnImpl column, Graph graph) { diff --git a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java index 1ca299b1..34862c94 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java @@ -16,11 +16,15 @@ package org.gephi.graph.impl; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import org.gephi.graph.api.Column; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; +import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; +import org.gephi.graph.api.types.TimestampIntegerMap; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; @@ -32,21 +36,25 @@ public class ColumnNoIndexTest { private GraphStore graphStore; private ColumnNoIndexImpl fooIndex; private ColumnNoIndexImpl ageIndex; + private ColumnNoIndexImpl priceIndex; @BeforeMethod public void setup() { graphStore = generateGraphStoreWithColumns(); Column col = graphStore.nodeTable.getColumn("foo"); Column col2 = graphStore.nodeTable.getColumn("age"); + Column col3 = graphStore.nodeTable.getColumn("price"); fooIndex = createIndex(graphStore, col.getId()); ageIndex = createIndex(graphStore, col2.getId()); + priceIndex = createIndex(graphStore, col3.getId()); } @AfterMethod public void cleanUp() { fooIndex = null; ageIndex = null; + priceIndex = null; graphStore = null; } @@ -177,6 +185,36 @@ public void testGetMinValueNotSortable() { fooIndex.getMinValue(); } + @Test + public void testDynamicAttribute() { + TimestampIntegerMap t = new TimestampIntegerMap(); + t.put(2000.0, 100); + t.put(2010.0, 150); + + addNodeWithAttribute(graphStore, priceIndex.column, "1", t); + + Assert.assertTrue(priceIndex.isSortable()); + Assert.assertEquals(priceIndex.getMinValue(), 100); + Assert.assertEquals(priceIndex.getMaxValue(), 100); + Assert.assertEquals(priceIndex.values(), Collections.singletonList(100)); + Assert.assertEquals(priceIndex.count(100), 1); + Assert.assertEquals(priceIndex.countElements(), 1); + Assert.assertEquals(priceIndex.countValues(), 1); + } + + @Test + public void testDynamicAttributeWithEstimator() { + TimestampIntegerMap t = new TimestampIntegerMap(); + t.put(2000.0, 100); + t.put(2010.0, 150); + + addNodeWithAttribute(graphStore, priceIndex.column, "1", t); + priceIndex.column.setEstimator(Estimator.AVERAGE); + + Assert.assertEquals(priceIndex.getMinValue(), 125.0); + Assert.assertEquals(priceIndex.getMaxValue(), 125.0); + } + @Test public void testVersion() { int version = fooIndex.getVersion(); @@ -206,6 +244,8 @@ private GraphStore generateGraphStoreWithColumns() { ColumnStore columnStore = graphStore.nodeTable.store; columnStore.addColumn(new ColumnImpl("foo", String.class, "foo", null, Origin.DATA, false, false)); columnStore.addColumn(new ColumnImpl("age", Integer.class, "Age", null, Origin.DATA, true, false)); + columnStore + .addColumn(new ColumnImpl("price", TimestampIntegerMap.class, "Price", null, Origin.DATA, true, false)); return graphStore; } diff --git a/src/test/java/org/gephi/graph/impl/TableImplTest.java b/src/test/java/org/gephi/graph/impl/TableImplTest.java index 49c647ae..a385d9b4 100644 --- a/src/test/java/org/gephi/graph/impl/TableImplTest.java +++ b/src/test/java/org/gephi/graph/impl/TableImplTest.java @@ -20,6 +20,7 @@ import org.gephi.graph.api.Column; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Node; +import org.gephi.graph.api.types.TimestampIntegerMap; import org.testng.Assert; import org.testng.annotations.Test; @@ -108,9 +109,13 @@ public void testIsIndexed() { TableImpl table = new TableImpl<>(graphStore, Node.class, true); Column col1 = table.addColumn("Id", null, Integer.class, Origin.DATA, null, false); Column col2 = table.addColumn("1", null, Integer.class, Origin.DATA, null, true); + Column col3 = table.addColumn("2", null, TimestampIntegerMap.class, Origin.DATA, null, true); + Column col4 = table.addColumn("3", null, Integer[].class, Origin.DATA, null, true); Assert.assertFalse(col1.isIndexed()); Assert.assertTrue(col2.isIndexed()); + Assert.assertTrue(col3.isIndexed()); + Assert.assertTrue(col4.isIndexed()); } @Test From 0abdf8886597d2e08f1284513cd9e1453490f425 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 14 Feb 2022 21:09:08 +0100 Subject: [PATCH 063/271] Check time representation when setting attributes --- .../org/gephi/graph/impl/ElementImpl.java | 2 + .../org/gephi/graph/impl/ElementImplTest.java | 41 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index 7cfc8511..d2831fa7 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -751,6 +751,7 @@ void checkType(Column column, Object value) { if (value != null) { Class typeClass = column.getTypeClass(); if (TimestampMap.class.isAssignableFrom(typeClass)) { + checkTimeRepresentationTimestamp(); if ((value instanceof Double && (!typeClass .equals(TimestampDoubleMap.class))) || (value instanceof Float && !typeClass .equals(TimestampFloatMap.class)) || (value instanceof Boolean && !typeClass @@ -765,6 +766,7 @@ void checkType(Column column, Object value) { "The object class does not match with the dynamic type (" + typeClass.getName() + ")"); } } else if (IntervalMap.class.isAssignableFrom(typeClass)) { + checkTimeRepresentationInterval(); if ((value instanceof Double && (!typeClass .equals(IntervalDoubleMap.class))) || (value instanceof Float && !typeClass .equals(IntervalFloatMap.class)) || (value instanceof Boolean && !typeClass diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index 5f58a1ee..7fb71cdc 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -199,6 +199,23 @@ public void testSetAttributeTimestamp() { Assert.assertEquals(node.getAttribute(column), ti); } + @Test + public void testSetAttributeInterval() { + GraphStore store = getIntervalGraphStore(); + Column column = generateIntervalColumn(store); + + IntervalIntegerMap ti = new IntervalIntegerMap(); + ti.put(new Interval(1.0, 2.0), 42); + ti.put(new Interval(2.0, 3.0), 10); + + NodeImpl node = new NodeImpl("0", store); + node.setAttribute(column, ti); + + Assert.assertEquals(node.attributes.length, 1 + getElementPropertiesLength()); + Assert.assertEquals(node.attributes[getFirstNonPropertyIndex()], ti); + Assert.assertEquals(node.getAttribute(column), ti); + } + @Test public void testSetAttributeTimeset() { GraphStore store = new GraphStore(); @@ -1054,6 +1071,29 @@ public void testCheckType() { node.checkType(new ColumnImpl("0", TimestampCharMap.class, null, null, Origin.DATA, false, false), 'a'); node.checkType(new ColumnImpl("0", TimestampBooleanMap.class, null, null, Origin.DATA, false, false), true); node.checkType(new ColumnImpl("0", TimestampStringMap.class, null, null, Origin.DATA, false, false), "foo"); + } + + @Test(expectedExceptions = RuntimeException.class) + public void testCheckTypeWithWrongIntervalConfiguration() { + GraphStore store = new GraphStore(); + + NodeImpl node = new NodeImpl("0", store); + node.checkType(new ColumnImpl("0", IntervalIntegerMap.class, null, null, Origin.DATA, false, false), 1); + } + + @Test(expectedExceptions = RuntimeException.class) + public void testCheckTypeWithWrongTimestampConfiguration() { + GraphStore store = getIntervalGraphStore(); + + NodeImpl node = new NodeImpl("0", store); + node.checkType(new ColumnImpl("0", TimestampIntegerMap.class, null, null, Origin.DATA, false, false), 1); + } + + @Test + public void testCheckTypeInterval() { + GraphStore store = getIntervalGraphStore(); + + NodeImpl node = new NodeImpl("0", store); node.checkType(new ColumnImpl("0", IntervalIntegerMap.class, null, null, Origin.DATA, false, false), 1); node.checkType(new ColumnImpl("0", IntervalDoubleMap.class, null, null, Origin.DATA, false, false), 1.0); node.checkType(new ColumnImpl("0", IntervalFloatMap.class, null, null, Origin.DATA, false, false), 1f); @@ -1063,7 +1103,6 @@ public void testCheckType() { node.checkType(new ColumnImpl("0", IntervalCharMap.class, null, null, Origin.DATA, false, false), 'a'); node.checkType(new ColumnImpl("0", IntervalBooleanMap.class, null, null, Origin.DATA, false, false), true); node.checkType(new ColumnImpl("0", IntervalStringMap.class, null, null, Origin.DATA, false, false), "foo"); - } @Test From 0e4ba24cd852cbf6ce96d422cdac5d2284aee530 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 14 Feb 2022 21:09:37 +0100 Subject: [PATCH 064/271] Disable spatial index for now until it's finalised --- .../org/gephi/graph/impl/GraphStoreConfiguration.java | 2 +- .../org/gephi/graph/impl/SpatialIndexImplTest.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java index 5eccc28f..ba7a3360 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java +++ b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java @@ -32,7 +32,7 @@ public final class GraphStoreConfiguration { public static final boolean ENABLE_NODE_PROPERTIES = true; public static final boolean ENABLE_EDGE_PROPERTIES = true; public static final boolean ENABLE_PARALLEL_EDGES = true; - public static final boolean ENABLE_SPATIAL_INDEX = true; + public static final boolean ENABLE_SPATIAL_INDEX = false; // NodeStore public final static int NODESTORE_BLOCK_SIZE = 5000; public final static int NODESTORE_DEFAULT_BLOCKS = 10; diff --git a/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java b/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java index 4fb67479..fbb0325f 100644 --- a/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java @@ -7,6 +7,9 @@ import org.gephi.graph.api.NodeIterable; import org.gephi.graph.api.Rect2D; import org.testng.Assert; +import org.testng.SkipException; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class SpatialIndexImplTest { @@ -14,6 +17,13 @@ public class SpatialIndexImplTest { private static final float BOUNDS = 1000f; private static final Rect2D BOUNDS_RECT = new Rect2D(-BOUNDS, -BOUNDS, BOUNDS, BOUNDS); + @BeforeMethod + public void setUp() { + if (!GraphStoreConfiguration.ENABLE_SPATIAL_INDEX) { + throw new SkipException("Skip spatial index tests because feature is disabled"); + } + } + @Test public void testGetEdgesEmpty() { SpatialIndexImpl spatialIndex = new GraphStore().spatialIndex; From 6511ff50808bf1b56e23fe1a9ad24f0a68c457e8 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 15 Feb 2022 22:09:28 +0100 Subject: [PATCH 065/271] Add degree index and make it easier to retrieve graph versions --- src/main/java/org/gephi/graph/api/Graph.java | 10 + .../gephi/graph/impl/DegreeNoIndexImpl.java | 222 ++++++++++++++++++ .../java/org/gephi/graph/impl/GraphStore.java | 6 + .../gephi/graph/impl/GraphViewDecorator.java | 5 + .../org/gephi/graph/impl/GraphViewImpl.java | 5 + .../gephi/graph/impl/UndirectedDecorator.java | 5 + .../org/gephi/graph/impl/BasicGraphStore.java | 5 + .../gephi/graph/impl/DegreeNoIndexTest.java | 106 +++++++++ .../org/gephi/graph/impl/GraphGenerator.java | 2 +- .../org/gephi/graph/impl/GraphStoreTest.java | 13 + 10 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/gephi/graph/impl/DegreeNoIndexImpl.java create mode 100644 src/test/java/org/gephi/graph/impl/DegreeNoIndexTest.java diff --git a/src/main/java/org/gephi/graph/api/Graph.java b/src/main/java/org/gephi/graph/api/Graph.java index a24aa67c..0de3634d 100644 --- a/src/main/java/org/gephi/graph/api/Graph.java +++ b/src/main/java/org/gephi/graph/api/Graph.java @@ -452,6 +452,16 @@ public interface Graph { */ public GraphModel getModel(); + /** + * Returns a version number for this graph. + *

+ * The version gets altered when the graph structure changes. + * + * @see GraphObserver for a more sophisticated way to track changes + * @return graph version + */ + public int getVersion(); + /** * Returns true if this graph is directed. * diff --git a/src/main/java/org/gephi/graph/impl/DegreeNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/DegreeNoIndexImpl.java new file mode 100644 index 00000000..2437e2f7 --- /dev/null +++ b/src/main/java/org/gephi/graph/impl/DegreeNoIndexImpl.java @@ -0,0 +1,222 @@ +package org.gephi.graph.impl; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import org.gephi.graph.api.Column; +import org.gephi.graph.api.DirectedGraph; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphLock; +import org.gephi.graph.api.Node; + +public class DegreeNoIndexImpl implements ColumnIndexImpl { + + // Type + public enum DegreeType { + DEGREE, IN_DEGREE, OUT_DEGREE + } + + // Type + protected final DegreeType degreeType; + // Graph + protected final Graph graph; + protected final GraphLock graphLock; + + protected DegreeNoIndexImpl(Graph graph, DegreeType degreeType) { + this.graph = graph; + this.graphLock = graph.getLock(); + this.degreeType = degreeType; + } + + @Override + public int count(Integer value) { + checkNull(value); + + Iterator nodeIterator = graph.getNodes().iterator(); + int count = 0; + while (nodeIterator.hasNext()) { + Node node = nodeIterator.next(); + int degree = getDegree(node); + if (value == degree) { + count++; + } + } + return count; + } + + @Override + public Iterable get(Integer degree) { + checkNull(degree); + return new NodeWithDegreeIterable(degree); + } + + @Override + public Collection values() { + Iterator nodeIterator = graph.getNodes().iterator(); + Set set = new ObjectOpenHashSet<>(); + while (nodeIterator.hasNext()) { + Node node = nodeIterator.next(); + int degree = getDegree(node); + set.add(degree); + } + return set; + } + + @Override + public int countValues() { + return values().size(); + } + + @Override + public int countElements() { + return graph.getNodeCount(); + } + + @Override + public boolean isSortable() { + return true; + } + + @Override + public Integer getMinValue() { + Integer min = null; + Iterator nodeIterator = graph.getNodes().iterator(); + int minN = Integer.MAX_VALUE; + while (nodeIterator.hasNext()) { + Node node = nodeIterator.next(); + int degree = getDegree(node); + if (min == null || (degree < minN)) { + minN = degree; + min = degree; + } + } + return min; + } + + @Override + public Integer getMaxValue() { + Integer max = null; + Iterator nodeIterator = graph.getNodes().iterator(); + int maxN = Integer.MIN_VALUE; + while (nodeIterator.hasNext()) { + Node node = nodeIterator.next(); + int degree = getDegree(node); + if (max == null || (degree > maxN)) { + maxN = degree; + max = degree; + } + } + return max; + } + + @Override + public Column getColumn() { + return null; + } + + @Override + public int getVersion() { + return ((GraphModelImpl) graph.getModel()).store.version.nodeVersion; + } + + @Override + public Iterator>> iterator() { + // TODO + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public void clear() { + // Nothing to clear + } + + @Override + public void destroy() { + // Nothing to destroy + } + + @Override + public Integer putValue(Node element, Integer value) { + return value; + } + + @Override + public Integer replaceValue(Node element, Integer oldValue, Integer newValue) { + return newValue; + } + + @Override + public void removeValue(Node element, Integer value) { + // Nothing to remove + } + + private int getDegree(Node node) { + switch (degreeType) { + case DEGREE: + return graph.getDegree(node); + case IN_DEGREE: + return ((DirectedGraph) graph).getInDegree(node); + case OUT_DEGREE: + return ((DirectedGraph) graph).getOutDegree(node); + } + throw new RuntimeException(); + } + + private void checkNull(Integer value) { + if (value == null) { + throw new NullPointerException(); + } + } + + private class NodeWithDegreeIterable implements Iterable { + + private final Integer value; + + public NodeWithDegreeIterable(Integer degree) { + this.value = degree; + } + + @Override + public Iterator iterator() { + return new NodeWithDegreeIterator(value); + } + } + + private class NodeWithDegreeIterator implements Iterator { + + private final Iterator itr; + private final Integer value; + private Node pointer; + + public NodeWithDegreeIterator(Integer value) { + this.itr = graph.getNodes().iterator(); + this.value = value; + } + + @Override + public boolean hasNext() { + while (pointer == null && itr.hasNext()) { + Node node = itr.next(); + int degree = getDegree(node); + if (value == degree) { + pointer = node; + } + } + return pointer != null; + } + + @Override + public Node next() { + Node res = pointer; + pointer = null; + return res; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Not supported."); + } + } +} diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 8a50fdce..9624a4e1 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Set; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Configuration; @@ -757,6 +758,11 @@ public Graph getRootGraph() { return this; } + @Override + public int getVersion() { + return Objects.hash(version.nodeVersion, version.edgeVersion); + } + protected GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { if (graph.getView() != mainGraphView) { throw new RuntimeException("This graph doesn't belong to this store"); diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 1b67d387..473096d3 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -596,6 +596,11 @@ public GraphModel getModel() { return graphStore.graphModel; } + @Override + public int getVersion() { + return view.getVersion(); + } + @Override public boolean isDirected() { return graphStore.isDirected(); diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index 9670e321..8fc0f18a 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Objects; import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; @@ -599,6 +600,10 @@ public boolean isDestroyed() { return storeId == GraphViewStore.NULL_VIEW; } + protected int getVersion() { + return Objects.hash(version.nodeVersion, version.edgeVersion); + } + protected GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { if (observers != null) { GraphObserverImpl observer = new GraphObserverImpl(graphStore, version, graph, withDiff); diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index e38b22fd..8216f9a7 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -355,6 +355,11 @@ public GraphModel getModel() { return store.graphModel; } + @Override + public int getVersion() { + return store.getVersion(); + } + @Override public boolean isDirected() { return false; diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index fa60b6b3..710c3662 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -315,6 +315,11 @@ public GraphModel getModel() { return null; } + @Override + public int getVersion() { + return 0; + } + @Override public void clearEdges(Node node) { BasicNode basicNode = (BasicNode) node; diff --git a/src/test/java/org/gephi/graph/impl/DegreeNoIndexTest.java b/src/test/java/org/gephi/graph/impl/DegreeNoIndexTest.java new file mode 100644 index 00000000..0ebde4dc --- /dev/null +++ b/src/test/java/org/gephi/graph/impl/DegreeNoIndexTest.java @@ -0,0 +1,106 @@ +package org.gephi.graph.impl; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.Node; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class DegreeNoIndexTest { + + @Test + public void testEmpty() { + GraphStore store = new GraphStore(); + DegreeNoIndexImpl index = new DegreeNoIndexImpl(store, DegreeNoIndexImpl.DegreeType.DEGREE); + Assert.assertEquals(index.countElements(), 0); + Assert.assertEquals(index.countValues(), 0); + Assert.assertEquals(index.count(0), 0); + Assert.assertFalse(index.get(0).iterator().hasNext()); + Assert.assertTrue(index.isSortable()); + Assert.assertNull(index.getColumn()); + Assert.assertNull(index.getMinValue()); + Assert.assertNull(index.getMaxValue()); + Assert.assertTrue(index.values().isEmpty()); + } + + @Test + public void testOneNode() { + GraphStore store = new GraphStore(); + Node node = store.factory.newNode(); + store.addNode(node); + DegreeNoIndexImpl index = new DegreeNoIndexImpl(store, DegreeNoIndexImpl.DegreeType.DEGREE); + Assert.assertEquals(index.countElements(), 1); + Assert.assertEquals(index.countValues(), 1); + Assert.assertEquals(index.count(0), 1); + Assert.assertEquals(index.getMinValue().intValue(), 0); + Assert.assertEquals(index.getMaxValue().intValue(), 0); + } + + @Test + public void testSmallGraph() { + Graph graph = GraphGenerator.generateTinyGraphStore(); + Node node = graph.getModel().factory().newNode(); + graph.addNode(node); + + DegreeNoIndexImpl index = new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.DEGREE); + Assert.assertEquals(index.countElements(), 3); + Assert.assertEquals(index.countValues(), 2); + Assert.assertEquals(index.count(1), 2); + Assert.assertEquals(index.getMinValue().intValue(), 0); + Assert.assertEquals(index.getMaxValue().intValue(), 1); + } + + @Test + public void testValues() { + Graph graph = GraphGenerator.generateTinyGraphStore(); + + DegreeNoIndexImpl index = new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.DEGREE); + Assert.assertEquals(index.values(), Collections.singletonList(1)); + } + + @Test + public void testGetIterator() { + Graph graph = GraphGenerator.generateTinyGraphStore(); + + DegreeNoIndexImpl index = new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.DEGREE); + Iterator itr = index.get(1).iterator(); + Assert.assertTrue(itr.hasNext()); + Assert.assertEquals(itr.next(), graph.getNode("1")); + Assert.assertTrue(itr.hasNext()); + Assert.assertEquals(itr.next(), graph.getNode("2")); + Assert.assertFalse(itr.hasNext()); + } + + @Test + public void testInDegree() { + Graph graph = GraphGenerator.generateTinyGraphStore(); + Edge edge = graph.getEdge("0"); + + DegreeNoIndexImpl index = new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.IN_DEGREE); + index.get(1).iterator().forEachRemaining(n -> Assert.assertSame(n, edge.getTarget())); + index.get(0).iterator().forEachRemaining(n -> Assert.assertSame(n, edge.getSource())); + } + + @Test + public void testOutDegree() { + Graph graph = GraphGenerator.generateTinyGraphStore(); + Edge edge = graph.getEdge("0"); + + DegreeNoIndexImpl index = new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.OUT_DEGREE); + index.get(0).iterator().forEachRemaining(n -> Assert.assertSame(n, edge.getTarget())); + index.get(1).iterator().forEachRemaining(n -> Assert.assertSame(n, edge.getSource())); + } + + @Test + public void testVersion() { + Graph graph = GraphGenerator.generateTinyGraphStore(); + + DegreeNoIndexImpl index = new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.DEGREE); + int version = index.getVersion(); + graph.removeNode(graph.getNode("1")); + Assert.assertNotEquals(index.getVersion(), version); + } +} diff --git a/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/src/test/java/org/gephi/graph/impl/GraphGenerator.java index 43cae8d6..97ab035e 100644 --- a/src/test/java/org/gephi/graph/impl/GraphGenerator.java +++ b/src/test/java/org/gephi/graph/impl/GraphGenerator.java @@ -403,7 +403,7 @@ public static GraphStore generateTinyUndirectedGraphStore() { GraphStore graphStore = graphModel.store; Node n1 = graphStore.factory.newNode("1"); Node n2 = graphStore.factory.newNode("2"); - graphStore.addAllNodes(Arrays.asList(new Node[] { n1, n2 })); + graphStore.addAllNodes(Arrays.asList(n1, n2)); Edge e0 = graphStore.factory.newEdge("0", n1, n2, EdgeTypeStore.NULL_LABEL, 1.0, false); graphStore.addEdge(e0); return graphStore; diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index f5c00f17..fdb6fe27 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -871,6 +871,19 @@ public void testEdgeIterableToArray() { Assert.assertEquals(edgeCollection, expected); } + @Test + public void testVersion() { + GraphStore graphStore = new GraphStore(); + int version = graphStore.getVersion(); + NodeImpl[] nodes = GraphGenerator.generateNodeList(2); + graphStore.addNode(nodes[0]); + Assert.assertNotEquals(graphStore.getVersion(), version); + graphStore.addNode(nodes[1]); + version = graphStore.getVersion(); + graphStore.addEdge(graphStore.factory.newEdge(nodes[0], nodes[1])); + Assert.assertNotEquals(graphStore.getVersion(), version); + } + // UTILITY private void testNodeIterable(NodeIterable iterable, NodeImpl[] nodes) { Set nodeSet = new HashSet<>(iterable.toCollection()); From 13e32099304ed607035a4760ed9454d636718e5b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 17 Feb 2022 13:41:52 +0100 Subject: [PATCH 066/271] Add getEdges(int type) method to Graph --- src/main/java/org/gephi/graph/api/Graph.java | 8 +++ .../java/org/gephi/graph/impl/EdgeStore.java | 47 ++++++++++++- .../java/org/gephi/graph/impl/GraphStore.java | 28 +++++--- .../gephi/graph/impl/GraphViewDecorator.java | 6 ++ .../gephi/graph/impl/UndirectedDecorator.java | 5 ++ .../org/gephi/graph/impl/BasicGraphStore.java | 10 +++ .../org/gephi/graph/impl/GraphStoreTest.java | 67 +++++++++++++++++++ 7 files changed, 162 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/Graph.java b/src/main/java/org/gephi/graph/api/Graph.java index 0de3634d..5d64337c 100644 --- a/src/main/java/org/gephi/graph/api/Graph.java +++ b/src/main/java/org/gephi/graph/api/Graph.java @@ -197,6 +197,14 @@ public interface Graph { */ public EdgeIterable getEdges(); + /** + * Gets all the edges of a particular type in the graph. + * + * @param type edge type + * @return an edge iterable over all edges of this type + */ + public EdgeIterable getEdges(int type); + /** * Gets all the self-loop edges in the graph. * diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 6890adff..4f21d5c9 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -37,7 +37,7 @@ public class EdgeStore implements Collection, EdgeIterable { // Const protected final static int NULL_ID = -1; protected final static int NODE_BITS = 31; - protected static final Iterator EMPTY_EDGE_ITERATOR = Collections. emptyList().iterator(); + protected static final Iterator EMPTY_EDGE_ITERATOR = Collections.emptyIterator(); // Data protected int size; @@ -337,6 +337,10 @@ public SelfLoopIterator iteratorSelfLoop() { return new SelfLoopIterator(); } + public EdgeTypeIterator iteratorType(final int type, final boolean undirected) { + return new EdgeTypeIterator(type, undirected); + } + public EdgeOutIterator edgeOutIterator(final Node node) { checkValidNodeObject(node); return new EdgeOutIterator((NodeImpl) node); @@ -756,6 +760,16 @@ public boolean containsId(final Object id) { return dictionary.containsKey(id); } + public boolean containsAnyType(NodeImpl source, NodeImpl target) { + int typeLength = longDictionary.length; + for (int i = 0; i < typeLength; i++) { + if (contains(source, target, i)) { + return true; + } + } + return false; + } + public boolean contains(NodeImpl source, NodeImpl target, int type) { checkNonNullObject(source); checkNonNullObject(target); @@ -1285,6 +1299,37 @@ public void remove() { } } + protected final class EdgeTypeIterator extends EdgeStoreIterator { + + private final int type; + private final boolean undirected; + + public EdgeTypeIterator(int type, boolean undirected) { + super(); + this.type = type; + this.undirected = undirected; + } + + @Override + public boolean hasNext() { + pointer = null; + while (pointer == null) { + if (!super.hasNext()) { + return false; + } + if (pointer.getType() != type || (undirected && isUndirectedToIgnore(pointer))) { + pointer = null; + } + } + return true; + } + + @Override + public void remove() { + EdgeStore.this.remove(pointer); + } + } + protected final class SelfLoopIterator extends EdgeStoreIterator { public SelfLoopIterator() { diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 9624a4e1..5714c1ee 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -159,14 +159,7 @@ public boolean addAllNodes(final Collection nodes) { public boolean addEdge(final Edge edge) { autoWriteLock(); try { - int type = edge.getType(); - if (edgeTypeStore != null && !edgeTypeStore.contains(type)) { - if (GraphStoreConfiguration.ENABLE_AUTO_TYPE_REGISTRATION) { - edgeTypeStore.addType(String.valueOf(type), type); - } else { - throw new RuntimeException("The type doesn't exist"); - } - } + registerEdgeType(edge); return edgeStore.add(edge); } finally { autoWriteUnlock(); @@ -177,12 +170,26 @@ public boolean addEdge(final Edge edge) { public boolean addAllEdges(Collection edges) { autoWriteLock(); try { + for (Edge edge : edges) { + registerEdgeType(edge); + } return edgeStore.addAll(edges); } finally { autoWriteUnlock(); } } + private void registerEdgeType(Edge edge) { + int type = edge.getType(); + if (edgeTypeStore != null && !edgeTypeStore.contains(type)) { + if (GraphStoreConfiguration.ENABLE_AUTO_TYPE_REGISTRATION) { + edgeTypeStore.addType(String.valueOf(type), type); + } else { + throw new RuntimeException("The type doesn't exist"); + } + } + } + @Override public NodeImpl getNode(final Object id) { autoReadLock(); @@ -233,6 +240,11 @@ public EdgeIterable getEdges() { return edgeStore; } + @Override + public EdgeIterable getEdges(int type) { + return new EdgeIterableWrapper(edgeStore.iteratorType(type, false)); + } + @Override public EdgeIterable getSelfLoops() { return new EdgeIterableWrapper(edgeStore.iteratorSelfLoop()); diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 473096d3..d3bfdc94 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -338,6 +338,12 @@ public EdgeIterable getEdges() { } } + @Override + public EdgeIterable getEdges(int type) { + return graphStore.getEdgeIterableWrapper(new UndirectedEdgeViewIterator( + graphStore.edgeStore.iteratorType(type, undirected))); + } + @Override public EdgeIterable getSelfLoops() { return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.iteratorSelfLoop())); diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 8216f9a7..189d5885 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -157,6 +157,11 @@ public EdgeIterable getEdges() { return store.getEdgeIterableWrapper(store.edgeStore.iteratorUndirected()); } + @Override + public EdgeIterable getEdges(int type) { + return store.getEdgeIterableWrapper(store.edgeStore.iteratorType(type, true)); + } + @Override public EdgeIterable getSelfLoops() { return store.getEdgeIterableWrapper(store.edgeStore.iteratorSelfLoop()); diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index 710c3662..2109b17b 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -212,6 +212,11 @@ public EdgeIterable getEdges() { return new EdgeIterableWrapper(edgeStore.iterator()); } + @Override + public EdgeIterable getEdges(int type) { + return new EdgeIterableWrapper(edgeStore.typeIterator(type)); + } + @Override public NodeIterable getNeighbors(Node node) { return new NodeIterableWrapper( @@ -1282,6 +1287,11 @@ public Iterator iterator() { return new BasicEdgeIterator(idToEdgeMap.values().iterator()); } + public Iterator typeIterator(int type) { + ObjectSet set = new ObjectLinkedOpenHashSet<>(); + return new BasicEdgeIterator(idToEdgeMap.values().stream().filter(e -> e.type == type).iterator()); + } + public Iterator outIterator(BasicNode node) { ObjectSet set = new ObjectLinkedOpenHashSet<>(); for (Object2ObjectMap col : node.outEdges.values()) { diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index fdb6fe27..20db901c 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -21,10 +21,12 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import java.awt.Color; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnIterable; import org.gephi.graph.api.DirectedSubgraph; @@ -483,6 +485,47 @@ public void testAddEdge() { Assert.assertTrue(c); } + @Test + public void testAddEdgeTypeRegistration() { + GraphStore graphStore = new GraphStore(); + NodeImpl[] nodes = GraphGenerator.generateNodeList(2); + graphStore.addAllNodes(Arrays.asList(nodes)); + + EdgeTypeStore typeStore = graphStore.edgeTypeStore; + Assert.assertFalse(typeStore.contains(1)); + + EdgeImpl edge = new EdgeImpl("0", nodes[0], nodes[1], 1, 1.0, true); + graphStore.addEdge(edge); + + Assert.assertTrue(typeStore.contains(1)); + Assert.assertTrue(typeStore.contains("1")); + } + + @Test + public void testAddAllEdges() { + GraphStore graphStore = new GraphStore(); + NodeImpl[] nodes = GraphGenerator.generateNodeList(2); + graphStore.addAllNodes(Arrays.asList(nodes)); + + EdgeImpl edge = new EdgeImpl("0", nodes[0], nodes[1], 0, 1.0, true); + graphStore.addAllEdges(Collections.singletonList(edge)); + + Assert.assertTrue(graphStore.contains(edge)); + } + + @Test + public void testAddAllEdgesTypeRegistration() { + GraphStore graphStore = new GraphStore(); + NodeImpl[] nodes = GraphGenerator.generateNodeList(2); + graphStore.addAllNodes(Arrays.asList(nodes)); + + EdgeImpl edge = new EdgeImpl("0", nodes[0], nodes[1], 1, 1.0, true); + graphStore.addAllEdges(Collections.singletonList(edge)); + + Assert.assertTrue(graphStore.edgeTypeStore.contains(1)); + Assert.assertTrue(graphStore.edgeTypeStore.contains("1")); + } + @Test public void testRemoveNodeWithEdges() { GraphStore graphStore = new GraphStore(); @@ -588,6 +631,30 @@ public void testGetEdges() { testEdgeIterable(edgeIterable, edges); } + @Test + public void testGetEdgesDefaultType() { + GraphStore graphStore = new GraphStore(); + NodeStore nodeStore = GraphGenerator.generateNodeStore(5); + EdgeImpl[] edges = GraphGenerator.generateEdgeList(nodeStore, 4, 0, true, true, false); + graphStore.addAllEdges(Arrays.asList(edges)); + EdgeIterable edgeIterable = graphStore.getEdges(0); + testEdgeIterable(edgeIterable, edges); + } + + @Test + public void testGetEdgesByType() { + GraphStore graphStore = new GraphStore(); + NodeStore nodeStore = GraphGenerator.generateNodeStore(15); + EdgeImpl[] edges = GraphGenerator.generateMultiTypeEdgeList(nodeStore, 4, 3, true, true); + graphStore.addAllEdges(Arrays.asList(edges)); + testEdgeIterable(graphStore.getEdges(0), Arrays.stream(edges).filter(e -> e.getType() == 0) + .toArray(EdgeImpl[]::new)); + testEdgeIterable(graphStore.getEdges(1), Arrays.stream(edges).filter(e -> e.getType() == 1) + .toArray(EdgeImpl[]::new)); + testEdgeIterable(graphStore.getEdges(2), Arrays.stream(edges).filter(e -> e.getType() == 2) + .toArray(EdgeImpl[]::new)); + } + @Test public void testGetNodeEdges() { GraphStore graphStore = new GraphStore(); From dbd46b8a1d54e0ec9389b270e5cc3183c5adfe91 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 17 Feb 2022 13:42:47 +0100 Subject: [PATCH 067/271] Fix issue with multi graph generator and failing tests --- .../org/gephi/graph/impl/EdgeTypeStore.java | 3 ++- .../org/gephi/graph/impl/EdgeStoreTest.java | 6 +++--- .../gephi/graph/impl/EdgeTypeStoreTest.java | 2 ++ .../org/gephi/graph/impl/GraphBridgeTest.java | 4 +++- .../org/gephi/graph/impl/GraphGenerator.java | 19 +++++++++++++++++-- .../graph/impl/GraphViewDecoratorTest.java | 17 +++++------------ .../gephi/graph/impl/GraphViewStoreTest.java | 8 ++------ 7 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java index 0e973312..8f325419 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java @@ -103,7 +103,8 @@ public boolean addType(final Object label, final int id) { if (foundId != NULL_SHORT && foundId != givenId) { throw new RuntimeException("This label '" + label + "' is already assigned to a different id"); } else if (idMap.containsKey(givenId)) { - if ((label == null && idMap.get(givenId) == null) || idMap.get(givenId).equals(label)) { + if ((label == null && idMap.get(givenId) == null) || (idMap.get(givenId) != null && idMap.get(givenId) + .equals(label))) { return false; } else { throw new RuntimeException("This id '" + id + "' is already assigned to a different label"); diff --git a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java index 488bb60d..3239b11b 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; import it.unimi.dsi.fastutil.ints.Int2IntMap; @@ -39,7 +40,6 @@ import org.testng.annotations.Test; /** - * * @author mbastian */ public class EdgeStoreTest { @@ -1588,9 +1588,9 @@ public void testNodeAdjacentAllTypes() { for (EdgeImpl edge : edges) { Assert.assertTrue(edgeStore.isAdjacent(edge.source, edge.target)); - if (!edge.isSelfLoop() && !edge.isMutual()) { + if (!edge.isSelfLoop() && !edgeStore.containsAnyType(edge.target, edge.source)) { Assert.assertFalse(edgeStore.isAdjacent(edge.target, edge.source)); - } else if (edge.isMutual()) { + } else if (edgeStore.containsAnyType(edge.target, edge.source)) { Assert.assertTrue(edgeStore.isAdjacent(edge.target, edge.source)); } } diff --git a/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java index 17e7a2c7..4bde021a 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java @@ -25,6 +25,8 @@ public void testDefaultSize() { EdgeTypeStore edgeTypeStore = new EdgeTypeStore(); Assert.assertEquals(edgeTypeStore.size(), 1); + Assert.assertTrue(edgeTypeStore.contains(0)); + Assert.assertTrue(edgeTypeStore.contains(null)); } @Test diff --git a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java index fffd719e..ab5017dd 100644 --- a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java @@ -242,7 +242,9 @@ public void testCopyPartial() { } } for (Integer typeId : typeIds) { - source.edgeTypeStore.addType(String.valueOf(typeId), typeId); + if (typeId != EdgeTypeStore.NULL_LABEL) { + source.edgeTypeStore.addType(String.valueOf(typeId), typeId); + } } new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); diff --git a/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/src/test/java/org/gephi/graph/impl/GraphGenerator.java index 97ab035e..5d1336b4 100644 --- a/src/test/java/org/gephi/graph/impl/GraphGenerator.java +++ b/src/test/java/org/gephi/graph/impl/GraphGenerator.java @@ -141,7 +141,7 @@ public static EdgeImpl[] generateEdgeList(NodeStore nodeStore, int edgeCount, in graphStore = nodeStore.viewStore.graphStore; } - int c = 0; + int c = type * edgeCount; while (idSet.size() < edgeCount) { int sourceId = r.nextInt(nodeCount); int targetId = r.nextInt(nodeCount); @@ -317,7 +317,7 @@ public static int[] distributeTypeCounts(int typeCount, int edgeCount) { res[i] = edgeCount - total; assert res[i] > 0; } else { - res[i] = (int) (ratio[i] / sum); + res[i] = (int) (edgeCount * ratio[i] / sum); total += res[i]; } } @@ -398,6 +398,10 @@ public static GraphStore generateTinyGraphStore() { return generateTinyGraphStore(GraphStoreConfiguration.DEFAULT_TIME_REPRESENTATION); } + public static GraphStore generateEmptyGraphStore() { + return generateEmptyGraphStore(GraphStoreConfiguration.DEFAULT_TIME_REPRESENTATION); + } + public static GraphStore generateTinyUndirectedGraphStore() { GraphModelImpl graphModel = new GraphModelImpl(new Configuration()); GraphStore graphStore = graphModel.store; @@ -421,6 +425,17 @@ public static GraphStore generateTinyGraphStore(Configuration configuration) { return graphStore; } + public static GraphStore generateEmptyGraphStore(Configuration configuration) { + GraphModelImpl graphModel = new GraphModelImpl(configuration); + return graphModel.store; + } + + public static GraphStore generateEmptyGraphStore(TimeRepresentation timeRepresentation) { + Configuration config = new Configuration(); + config.setTimeRepresentation(timeRepresentation); + return generateEmptyGraphStore(config); + } + public static GraphStore generateTinyGraphStore(TimeRepresentation timeRepresentation) { Configuration config = new Configuration(); config.setTimeRepresentation(timeRepresentation); diff --git a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java index 61aea4dd..658e12aa 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java @@ -371,9 +371,7 @@ public void testDirectedClearEdges() { for (Edge e : graphStore.getEdges()) { Assert.assertFalse(graph.contains(e)); } - for (int i = 0; i < graphStore.edgeTypeStore.length; i++) { - Assert.assertEquals(graph.getEdgeCount(i), 0); - } + Assert.assertEquals(graph.getEdgeCount(0), 0); } @Test @@ -391,9 +389,7 @@ public void testUndirectedClearEdges() { for (Edge e : graphStore.getEdges()) { Assert.assertFalse(graph.contains(e)); } - for (int i = 0; i < graphStore.edgeTypeStore.length; i++) { - Assert.assertEquals(graph.getEdgeCount(i), 0); - } + Assert.assertEquals(graph.getEdgeCount(0), 0); } @Test @@ -415,9 +411,8 @@ public void testDirectedClear() { for (Node n : graphStore.getNodes()) { Assert.assertFalse(graph.contains(n)); } - for (int i = 0; i < graphStore.edgeTypeStore.length; i++) { - Assert.assertEquals(graph.getEdgeCount(i), 0); - } + + Assert.assertEquals(graph.getEdgeCount(0), 0); } @Test @@ -439,9 +434,7 @@ public void testUndirectedClear() { for (Node n : graphStore.getNodes()) { Assert.assertFalse(graph.contains(n)); } - for (int i = 0; i < graphStore.edgeTypeStore.length; i++) { - Assert.assertEquals(graph.getEdgeCount(i), 0); - } + Assert.assertEquals(graph.getEdgeCount(0), 0); } @Test diff --git a/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java index 12f4434d..eb6561a1 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java @@ -247,9 +247,7 @@ public void testDirectedEmptyView() { Assert.assertEquals(graph.getNodeCount(), 0); Assert.assertEquals(graph.getEdgeCount(), 0); - for (int i = 0; i < graphStore.edgeTypeStore.length; i++) { - Assert.assertEquals(graph.getEdgeCount(i), 0); - } + Assert.assertEquals(graph.getEdgeCount(0), 0); Assert.assertFalse(graph.getNodes().iterator().hasNext()); Assert.assertFalse(graph.getEdges().iterator().hasNext()); @@ -266,9 +264,7 @@ public void testUndirectedEmptyView() { Assert.assertEquals(graph.getNodeCount(), 0); Assert.assertEquals(graph.getEdgeCount(), 0); - for (int i = 0; i < graphStore.edgeTypeStore.length; i++) { - Assert.assertEquals(graph.getEdgeCount(i), 0); - } + Assert.assertEquals(graph.getEdgeCount(0), 0); Assert.assertFalse(graph.getNodes().iterator().hasNext()); Assert.assertFalse(graph.getEdges().iterator().hasNext()); From 0deb333bdcafb1bd6616e7e36e92615957ffdcc8 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 17 Feb 2022 13:43:21 +0100 Subject: [PATCH 068/271] Add default columns utility to GraphModel --- .../java/org/gephi/graph/api/GraphModel.java | 95 +++++++++++++++++++ .../gephi/graph/impl/DefaultColumnsImpl.java | 80 ++++++++++++++++ .../org/gephi/graph/impl/GraphModelImpl.java | 7 ++ .../graph/impl/GraphStoreConfiguration.java | 4 + .../org/gephi/graph/impl/GraphModelTest.java | 19 ++++ 5 files changed, 205 insertions(+) create mode 100644 src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index 5ed138f5..084680a2 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -169,6 +169,92 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce } } + /** + * Default columns utility. + */ + public static interface DefaultColumns { + + /** + * Return node identifier column. + *

+ * This is a read-only column. + * + * @return node id column + */ + public Column nodeId(); + + /** + * Return edge identifier column. + *

+ * This is a read-only column. + * + * @return edge id column + */ + public Column edgeId(); + + /** + * Return node label column. + * + * @return node label column + */ + public Column nodeLabel(); + + /** + * Return edge label column. + * + * @return edge label column + */ + public Column edgeLabel(); + + /** + * Return node time-set (timestamp or interval) column. + * + * @return node time-set column + */ + public Column nodeTimeSet(); + + /** + * Return edge time-set (timestamp or interval) column. + * + * @return edge time-set column + */ + public Column edgeTimeSet(); + + /** + * Return node degree column. + * + * @return node degree column + */ + public Column degree(); + + /** + * Return node in-degree column. + *

+ * Only for directed graphs. + * + * @return node in-degree column + */ + public Column inDegree(); + + /** + * Return node out-degree column. + *

+ * Only for directed graphs. + * + * @return node out-degree column + */ + public Column outDegree(); + + /** + * Return edge type column. + *

+ * Only for multi-graphs. + * + * @return node in-degree column + */ + public Column edgeType(); + } + /** * Returns the graph factory. * @@ -267,6 +353,15 @@ public static void write(DataOutput output, GraphModel graphModel) throws IOExce */ public void setVisibleView(GraphView view); + /** + * Returns the default columns. + *

+ * Default columns are always available for each element. + * + * @return default columns + */ + public DefaultColumns defaultColumns(); + /** * Adds a new edge type and returns the integer identifier. *

diff --git a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java new file mode 100644 index 00000000..a223d465 --- /dev/null +++ b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java @@ -0,0 +1,80 @@ +package org.gephi.graph.impl; + +import org.gephi.graph.api.Column; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.Origin; + +public class DefaultColumnsImpl implements GraphModel.DefaultColumns { + + protected final GraphStore store; + + // Extra columns (temporary solution, until they are fully added as normal + // columns) + protected final ColumnImpl degreeColumn; + protected final ColumnImpl inDegreeColumn; + protected final ColumnImpl outDegreeColumn; + protected final ColumnImpl typeColumn; + + public DefaultColumnsImpl(GraphStore store) { + this.store = store; + + degreeColumn = new ColumnImpl(store.nodeTable, GraphStoreConfiguration.NODE_DEGREE_COLUMN_ID, Integer.class, + "Degree", null, Origin.PROPERTY, false, true); + inDegreeColumn = new ColumnImpl(store.nodeTable, GraphStoreConfiguration.NODE_IN_DEGREE_COLUMN_ID, + Integer.class, "In-Degree", null, Origin.PROPERTY, false, true); + outDegreeColumn = new ColumnImpl(store.nodeTable, GraphStoreConfiguration.NODE_OUT_DEGREE_COLUMN_ID, + Integer.class, "Out-Degree", null, Origin.PROPERTY, false, true); + typeColumn = new ColumnImpl(store.edgeTable, GraphStoreConfiguration.EDGE_TYPE_COLUMN_ID, Integer.class, "Type", + null, Origin.PROPERTY, false, true); + } + + @Override + public Column nodeId() { + return store.nodeTable.getColumn(GraphStoreConfiguration.ELEMENT_ID_INDEX); + } + + @Override + public Column edgeId() { + return store.nodeTable.getColumn(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); + } + + @Override + public Column nodeLabel() { + return store.nodeTable.getColumn(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); + } + + @Override + public Column edgeLabel() { + return store.edgeTable.getColumn(GraphStoreConfiguration.ELEMENT_ID_INDEX); + } + + @Override + public Column nodeTimeSet() { + return store.nodeTable.getColumn(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); + } + + @Override + public Column edgeTimeSet() { + return store.edgeTable.getColumn(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); + } + + @Override + public Column degree() { + return degreeColumn; + } + + @Override + public Column inDegree() { + return inDegreeColumn; + } + + @Override + public Column outDegree() { + return outDegreeColumn; + } + + @Override + public Column edgeType() { + return typeColumn; + } +} diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index ce97bb48..0caa9bd2 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -51,6 +51,7 @@ public class GraphModelImpl implements GraphModel { protected final Configuration configuration; protected final GraphStore store; protected final GraphBridgeImpl graphBridge; + protected final DefaultColumnsImpl defaultColumns; public GraphModelImpl() { this(new Configuration()); @@ -62,6 +63,7 @@ public GraphModelImpl(Configuration config) { configuration = config.copy(); store = new GraphStore(this); graphBridge = new GraphBridgeImpl(store); + defaultColumns = new DefaultColumnsImpl(store); } @Override @@ -137,6 +139,11 @@ public void setVisibleView(GraphView view) { } } + @Override + public DefaultColumns defaultColumns() { + return defaultColumns; + } + @Override public int addEdgeType(Object label) { store.autoWriteLock(); diff --git a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java index ba7a3360..897dd625 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java +++ b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java @@ -63,6 +63,10 @@ public final class GraphStoreConfiguration { public static final String ELEMENT_LABEL_COLUMN_ID = "label"; public static final String ELEMENT_TIMESET_COLUMN_ID = "timeset"; public static final String EDGE_WEIGHT_COLUMN_ID = "weight"; + public static final String NODE_DEGREE_COLUMN_ID = "degree"; + public static final String NODE_IN_DEGREE_COLUMN_ID = "indegree"; + public static final String NODE_OUT_DEGREE_COLUMN_ID = "outdegree"; + public static final String EDGE_TYPE_COLUMN_ID = "type"; // Properties index public static final int ELEMENT_ID_INDEX = 0; public static final int ELEMENT_LABEL_INDEX = 1; diff --git a/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/src/test/java/org/gephi/graph/impl/GraphModelTest.java index 35e7947f..37b8b664 100644 --- a/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -722,4 +722,23 @@ public void testNodeAttributesAddAndClearColumns() { Assert.assertNull(n1.getAttribute(col2)); } + + @Test + public void testDefaultColumns() { + GraphModelImpl graphModel = new GraphModelImpl(); + GraphModel.DefaultColumns defaultColumns = graphModel.defaultColumns(); + Assert.assertNotNull(defaultColumns); + + Assert.assertNotNull(defaultColumns.degree()); + Assert.assertNotNull(defaultColumns.inDegree()); + Assert.assertNotNull(defaultColumns.outDegree()); + Assert.assertNotNull(defaultColumns.nodeId()); + Assert.assertNotNull(defaultColumns.edgeId()); + Assert.assertNotNull(defaultColumns.nodeLabel()); + Assert.assertNotNull(defaultColumns.edgeLabel()); + Assert.assertNotNull(defaultColumns.nodeTimeSet()); + Assert.assertNotNull(defaultColumns.edgeTimeSet()); + Assert.assertNotNull(defaultColumns.edgeType()); + + } } From 3fd872fc33b2d3b5a45eced712107740c2a517ef Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 17 Feb 2022 13:44:11 +0100 Subject: [PATCH 069/271] Return default column in DegreeNoIndexImpl --- .../java/org/gephi/graph/impl/DegreeNoIndexImpl.java | 11 ++++++++--- .../java/org/gephi/graph/impl/DegreeNoIndexTest.java | 5 ++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/DegreeNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/DegreeNoIndexImpl.java index 2437e2f7..b1adc652 100644 --- a/src/main/java/org/gephi/graph/impl/DegreeNoIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/DegreeNoIndexImpl.java @@ -8,7 +8,6 @@ import org.gephi.graph.api.Column; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.Node; public class DegreeNoIndexImpl implements ColumnIndexImpl { @@ -22,11 +21,9 @@ public enum DegreeType { protected final DegreeType degreeType; // Graph protected final Graph graph; - protected final GraphLock graphLock; protected DegreeNoIndexImpl(Graph graph, DegreeType degreeType) { this.graph = graph; - this.graphLock = graph.getLock(); this.degreeType = degreeType; } @@ -113,6 +110,14 @@ public Integer getMaxValue() { @Override public Column getColumn() { + switch (degreeType) { + case DEGREE: + return graph.getModel().defaultColumns().degree(); + case IN_DEGREE: + return graph.getModel().defaultColumns().inDegree(); + case OUT_DEGREE: + return graph.getModel().defaultColumns().outDegree(); + } return null; } diff --git a/src/test/java/org/gephi/graph/impl/DegreeNoIndexTest.java b/src/test/java/org/gephi/graph/impl/DegreeNoIndexTest.java index 0ebde4dc..8d5df62a 100644 --- a/src/test/java/org/gephi/graph/impl/DegreeNoIndexTest.java +++ b/src/test/java/org/gephi/graph/impl/DegreeNoIndexTest.java @@ -1,6 +1,5 @@ package org.gephi.graph.impl; -import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import org.gephi.graph.api.Edge; @@ -13,14 +12,14 @@ public class DegreeNoIndexTest { @Test public void testEmpty() { - GraphStore store = new GraphStore(); + GraphStore store = GraphGenerator.generateEmptyGraphStore(); DegreeNoIndexImpl index = new DegreeNoIndexImpl(store, DegreeNoIndexImpl.DegreeType.DEGREE); Assert.assertEquals(index.countElements(), 0); Assert.assertEquals(index.countValues(), 0); Assert.assertEquals(index.count(0), 0); Assert.assertFalse(index.get(0).iterator().hasNext()); Assert.assertTrue(index.isSortable()); - Assert.assertNull(index.getColumn()); + Assert.assertSame(index.getColumn(), store.getModel().defaultColumns().degree()); Assert.assertNull(index.getMinValue()); Assert.assertNull(index.getMaxValue()); Assert.assertTrue(index.values().isEmpty()); From 19c1ff05f1d2b0eaae7ccf39c8034d4c67810f1a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 17 Feb 2022 13:44:25 +0100 Subject: [PATCH 070/271] Add EdgeTypeNotIndex --- .../gephi/graph/impl/EdgeTypeNoIndexImpl.java | 109 ++++++++++++++++++ .../gephi/graph/impl/EdgeTypeNoIndexTest.java | 87 ++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 src/main/java/org/gephi/graph/impl/EdgeTypeNoIndexImpl.java create mode 100644 src/test/java/org/gephi/graph/impl/EdgeTypeNoIndexTest.java diff --git a/src/main/java/org/gephi/graph/impl/EdgeTypeNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/EdgeTypeNoIndexImpl.java new file mode 100644 index 00000000..d47f2205 --- /dev/null +++ b/src/main/java/org/gephi/graph/impl/EdgeTypeNoIndexImpl.java @@ -0,0 +1,109 @@ +package org.gephi.graph.impl; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import org.gephi.graph.api.Column; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; + +public class EdgeTypeNoIndexImpl implements ColumnIndexImpl { + + // Graph + protected final Graph graph; + + protected EdgeTypeNoIndexImpl(Graph graph) { + this.graph = graph; + } + + @Override + public int count(Object label) { + return graph.getEdgeCount(labelToType(label)); + } + + @Override + public Iterable get(Object label) { + return graph.getEdges(labelToType(label)); + } + + @Override + public Collection values() { + return Arrays.asList(graph.getModel().getEdgeTypeLabels(false)); + } + + @Override + public int countValues() { + return values().size(); + } + + @Override + public int countElements() { + return graph.getEdgeCount(); + } + + @Override + public boolean isSortable() { + return false; + } + + @Override + public Number getMinValue() { + throw new UnsupportedOperationException("Edge type index is not sortable"); + } + + @Override + public Number getMaxValue() { + throw new UnsupportedOperationException("Edge type index is not sortable"); + } + + @Override + public Column getColumn() { + return graph.getModel().defaultColumns().edgeType(); + } + + @Override + public int getVersion() { + return ((GraphModelImpl) graph.getModel()).store.version.edgeVersion; + } + + @Override + public Iterator>> iterator() { + // TODO + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public void clear() { + // Nothing to clear + } + + @Override + public void destroy() { + // Nothing to destroy + } + + @Override + public Object putValue(Edge element, Object value) { + return value; + } + + @Override + public Object replaceValue(Edge element, Object oldValue, Object newValue) { + return newValue; + } + + @Override + public void removeValue(Edge element, Object value) { + // Nothing to remove + } + + private int labelToType(Object label) { + int type = graph.getModel().getEdgeType(label); + if (type == -1) { + throw new IllegalArgumentException("Edge label " + label + " doesn't exist"); + } + return type; + } +} diff --git a/src/test/java/org/gephi/graph/impl/EdgeTypeNoIndexTest.java b/src/test/java/org/gephi/graph/impl/EdgeTypeNoIndexTest.java new file mode 100644 index 00000000..782f4073 --- /dev/null +++ b/src/test/java/org/gephi/graph/impl/EdgeTypeNoIndexTest.java @@ -0,0 +1,87 @@ +package org.gephi.graph.impl; + +import java.util.Collections; +import java.util.Iterator; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class EdgeTypeNoIndexTest { + + @Test + public void testEmpty() { + GraphStore store = GraphGenerator.generateEmptyGraphStore(); + EdgeTypeNoIndexImpl index = new EdgeTypeNoIndexImpl(store); + Assert.assertEquals(index.countElements(), 0); + Assert.assertEquals(index.countValues(), 0); + Assert.assertEquals(index.count(null), 0); + Assert.assertFalse(index.get(null).iterator().hasNext()); + Assert.assertFalse(index.isSortable()); + Assert.assertSame(index.getColumn(), store.getModel().defaultColumns().edgeType()); + Assert.assertTrue(index.values().isEmpty()); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testMinValueException() { + GraphStore store = new GraphStore(); + EdgeTypeNoIndexImpl index = new EdgeTypeNoIndexImpl(store); + Assert.assertNull(index.getMinValue()); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testMaxValueException() { + GraphStore store = new GraphStore(); + EdgeTypeNoIndexImpl index = new EdgeTypeNoIndexImpl(store); + Assert.assertNull(index.getMaxValue()); + } + + @Test + public void testOneEdge() { + GraphStore store = GraphGenerator.generateTinyGraphStore(); + EdgeTypeNoIndexImpl index = new EdgeTypeNoIndexImpl(store); + Assert.assertEquals(index.countElements(), 1); + Assert.assertEquals(index.countValues(), 1); + Assert.assertEquals(index.count(null), 1); + } + + @Test + public void testSmallGraph() { + Graph graph = GraphGenerator.generateSmallMultiTypeGraphStore(); + + EdgeTypeNoIndexImpl index = new EdgeTypeNoIndexImpl(graph); + Assert.assertEquals(index.countElements(), graph.getEdgeCount()); + Assert.assertEquals(index.countValues(), 3); + Assert.assertEquals(index.count(null), graph.getEdgeCount(0)); + Assert.assertEquals(index.count("1"), graph.getEdgeCount(1)); + } + + @Test + public void testValues() { + Graph graph = GraphGenerator.generateTinyGraphStore(); + + EdgeTypeNoIndexImpl index = new EdgeTypeNoIndexImpl(graph); + Assert.assertEquals(index.values(), Collections.singletonList(null)); + } + + @Test + public void testGetIterator() { + Graph graph = GraphGenerator.generateTinyGraphStore(); + + EdgeTypeNoIndexImpl index = new EdgeTypeNoIndexImpl(graph); + Iterator itr = index.get(null).iterator(); + Assert.assertTrue(itr.hasNext()); + Assert.assertEquals(itr.next(), graph.getEdge("0")); + Assert.assertFalse(itr.hasNext()); + } + + @Test + public void testVersion() { + Graph graph = GraphGenerator.generateTinyGraphStore(); + + EdgeTypeNoIndexImpl index = new EdgeTypeNoIndexImpl(graph); + int version = index.getVersion(); + graph.removeEdge(graph.getEdge("0")); + Assert.assertNotEquals(index.getVersion(), version); + } +} From 23cbfff33e4fabfaa45cd4c6148fe9b363d986d4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 17 Feb 2022 14:01:13 +0100 Subject: [PATCH 071/271] Allow retrieval of degree and edge type index based on default column --- .../gephi/graph/impl/DefaultColumnsImpl.java | 4 +-- .../java/org/gephi/graph/impl/IndexImpl.java | 14 +++++++++++ .../org/gephi/graph/impl/ElementImplTest.java | 15 +++++++++++ .../org/gephi/graph/impl/IndexImplTest.java | 25 ++++++++++++++++--- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java index a223d465..11973f04 100644 --- a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java +++ b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java @@ -35,7 +35,7 @@ public Column nodeId() { @Override public Column edgeId() { - return store.nodeTable.getColumn(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); + return store.edgeTable.getColumn(GraphStoreConfiguration.ELEMENT_ID_INDEX); } @Override @@ -45,7 +45,7 @@ public Column nodeLabel() { @Override public Column edgeLabel() { - return store.edgeTable.getColumn(GraphStoreConfiguration.ELEMENT_ID_INDEX); + return store.edgeTable.getColumn(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); } @Override diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index a6cd4224..216740fc 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -250,6 +250,20 @@ protected ColumnIndexImpl getIndex(Column col) { return index; } } + + // TODO: Make this more robust + if (col.isProperty()) { + DefaultColumnsImpl defaultColumns = columnStore.graphStore.graphModel.defaultColumns; + if (col == defaultColumns.degreeColumn) { + return new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.DEGREE); + } else if (col == defaultColumns.inDegreeColumn) { + return new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.IN_DEGREE); + } else if (col == defaultColumns.outDegreeColumn) { + return new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.OUT_DEGREE); + } else if (col == defaultColumns.typeColumn) { + return new EdgeTypeNoIndexImpl(graph); + } + } return null; } diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index 7fb71cdc..d6757abe 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -435,6 +435,21 @@ public void testGetAttributeKey() { Assert.assertEquals(res, 1); } + @Test + public void testGetAttributeDefaultColumns() { + GraphStore store = GraphGenerator.generateTinyGraphStore(); + Node node = store.getNode("1"); + Edge edge = store.getEdge("0"); + + Assert.assertEquals(node.getAttribute(store.getModel().defaultColumns().nodeId()), "1"); + Assert.assertNull(node.getAttribute(store.getModel().defaultColumns().nodeLabel())); + Assert.assertNull(node.getAttribute(store.getModel().defaultColumns().nodeTimeSet())); + + Assert.assertEquals(edge.getAttribute(store.getModel().defaultColumns().edgeId()), "0"); + Assert.assertNull(edge.getAttribute(store.getModel().defaultColumns().edgeLabel())); + Assert.assertNull(edge.getAttribute(store.getModel().defaultColumns().edgeTimeSet())); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testGetAttributeKeyUnknown() { GraphStore store = new GraphStore(); diff --git a/src/test/java/org/gephi/graph/impl/IndexImplTest.java b/src/test/java/org/gephi/graph/impl/IndexImplTest.java index 1d7ef8ea..13babeb9 100644 --- a/src/test/java/org/gephi/graph/impl/IndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexImplTest.java @@ -16,6 +16,7 @@ package org.gephi.graph.impl; +import org.gephi.graph.api.Edge; import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; import org.testng.Assert; @@ -110,9 +111,27 @@ public void testDestroy() { Assert.assertNull(index.getIndex(col2)); } + @Test + public void testDefaultColumns() { + GraphStore graphStore = GraphGenerator.generateEmptyGraphStore(); + IndexImpl nodeIndex = graphStore.nodeTable.store.indexStore.mainIndex; + IndexImpl edgeIndex = graphStore.edgeTable.store.indexStore.mainIndex; + + Assert.assertNotNull(nodeIndex.getIndex(graphStore.getModel().defaultColumns().degree())); + Assert.assertNotNull(nodeIndex.getIndex(graphStore.getModel().defaultColumns().inDegree())); + Assert.assertNotNull(nodeIndex.getIndex(graphStore.getModel().defaultColumns().outDegree())); + Assert.assertNotNull(nodeIndex.getIndex(graphStore.getModel().defaultColumns().nodeId())); + Assert.assertNotNull(nodeIndex.getIndex(graphStore.getModel().defaultColumns().nodeLabel())); + Assert.assertNotNull(nodeIndex.getIndex(graphStore.getModel().defaultColumns().nodeTimeSet())); + + Assert.assertNotNull(edgeIndex.getIndex(graphStore.getModel().defaultColumns().edgeId())); + Assert.assertNotNull(edgeIndex.getIndex(graphStore.getModel().defaultColumns().edgeLabel())); + Assert.assertNotNull(edgeIndex.getIndex(graphStore.getModel().defaultColumns().edgeType())); + Assert.assertNotNull(edgeIndex.getIndex(graphStore.getModel().defaultColumns().edgeTimeSet())); + } + private ColumnStore generateEmptyNodeStore() { - GraphStore graphStore = new GraphStore(); - ColumnStore columnStore = graphStore.nodeTable.store; - return columnStore; + GraphStore graphStore = GraphGenerator.generateEmptyGraphStore(); + return graphStore.nodeTable.store; } } From 743cfc46543a3e81801c068735044808a80ea150 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 17 Feb 2022 14:08:59 +0100 Subject: [PATCH 072/271] Release version 0.6.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 04dceb17..c55da7ee 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.3-SNAPSHOT + 0.6.3 jar GraphStore From 966a26867d1e9b5377145d9dd6e16f748738223a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 17 Feb 2022 14:14:38 +0100 Subject: [PATCH 073/271] Set version to 0.6.4-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c55da7ee..d4fd74ed 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.3 + 0.6.4-SNAPSHOT jar GraphStore From 5060cf4cc2ccfeb6c6c70fa6c7b0f9a2f75ed6a3 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 17 Feb 2022 19:29:11 +0100 Subject: [PATCH 074/271] Fix issue with index version increment --- .../org/gephi/graph/impl/ElementImpl.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index d2831fa7..412d4034 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -357,7 +357,9 @@ public void setAttribute(Column column, Object value) { } } } - } else if (column.isIndexed() && columnStore != null && isValid()) { + } + + if (column.isIndexed() && columnStore != null && isValid()) { value = columnStore.indexStore.set(column, oldValue, value, this); } attributes[index] = value; @@ -397,6 +399,7 @@ private void setTimeAttribute(Column column, Object value, Object timeObject) { checkType(column, value); int index = column.getIndex(); + ColumnStore columnStore = getColumnStore(); Object oldValue = null; boolean res; synchronized (this) { @@ -422,12 +425,16 @@ private void setTimeAttribute(Column column, Object value, Object timeObject) { } res = dynamicValue.put(timeObject, value); - } - if (res && isValid()) { - TimeIndexStore timeIndexStore = getTimeIndexStore(); - if (timeIndexStore != null) { - timeIndexStore.add(timeObject); + if (res && isValid()) { + TimeIndexStore timeIndexStore = getTimeIndexStore(); + if (timeIndexStore != null) { + timeIndexStore.add(timeObject); + } + + if (column.isIndexed() && columnStore != null) { + columnStore.indexStore.set(column, oldValue, dynamicValue, this); + } } } if (isValid()) { From 51c8a81b9b60888c5f9e6e2100260aedd9ad82ee Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 18 Feb 2022 21:19:26 +0100 Subject: [PATCH 075/271] Fix exception label --- src/main/java/org/gephi/graph/api/AttributeUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index 9a4f2b21..0b3d458d 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -463,7 +463,7 @@ public static Object parse(String str, Class typeClass, DateTimeZone timeZone) { return ArraysParser.parseArray(typeClass, str); } - throw new IllegalArgumentException("Unsupported type " + typeClass.getClass().getCanonicalName()); + throw new IllegalArgumentException("Unsupported type " + typeClass.getCanonicalName()); } /** From a521bcbd44c7f1127eb66833605a6aa7b1faba9f Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 31 Mar 2022 22:04:24 +0200 Subject: [PATCH 076/271] Add edge weight to default columns --- src/main/java/org/gephi/graph/api/GraphModel.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index 084680a2..78e9ee8f 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -206,6 +206,13 @@ public static interface DefaultColumns { */ public Column edgeLabel(); + /** + * Return edge weigth column. + * + * @return edge weight column + */ + public Column edgeWeight(); + /** * Return node time-set (timestamp or interval) column. * From c02a359f3ff31c4df2b109c83fcb4aac8fdb8567 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 31 Mar 2022 22:05:01 +0200 Subject: [PATCH 077/271] Add edge weight column impl --- src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java index 11973f04..24d7da5a 100644 --- a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java +++ b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java @@ -38,6 +38,10 @@ public Column edgeId() { return store.edgeTable.getColumn(GraphStoreConfiguration.ELEMENT_ID_INDEX); } + public Column edgeWeight() { + return store.edgeTable.getColumn(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + } + @Override public Column nodeLabel() { return store.nodeTable.getColumn(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); From 89feb97f29d1fa4984d2259e5fcd66c5bcc4c8b5 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 31 Mar 2022 22:07:46 +0200 Subject: [PATCH 078/271] Refactor attributes manipulation in Element and implement #148 --- .../java/org/gephi/graph/api/Element.java | 2 +- .../org/gephi/graph/impl/AttributesImpl.java | 256 ++++++++++ .../org/gephi/graph/impl/ColumnStore.java | 63 +-- .../java/org/gephi/graph/impl/EdgeImpl.java | 154 ++---- .../java/org/gephi/graph/impl/EdgeStore.java | 76 ++- .../org/gephi/graph/impl/ElementImpl.java | 453 +++++------------- .../java/org/gephi/graph/impl/GraphStore.java | 17 +- .../java/org/gephi/graph/impl/IndexStore.java | 29 +- .../java/org/gephi/graph/impl/NodeImpl.java | 2 - .../java/org/gephi/graph/impl/NodeStore.java | 7 +- .../org/gephi/graph/impl/Serialization.java | 8 +- .../java/org/gephi/graph/impl/TableImpl.java | 2 +- .../org/gephi/graph/impl/ColumnStoreTest.java | 12 - .../org/gephi/graph/impl/EdgeImplTest.java | 12 + .../org/gephi/graph/impl/ElementImplTest.java | 89 ++-- .../org/gephi/graph/impl/GraphModelTest.java | 1 - .../org/gephi/graph/impl/IndexStoreTest.java | 45 +- .../graph/impl/IntervalIndexStoreTest.java | 3 +- .../gephi/graph/impl/SerializationTest.java | 2 +- .../org/gephi/graph/impl/TableImplTest.java | 8 - .../graph/impl/TimestampIndexStoreTest.java | 2 +- 21 files changed, 603 insertions(+), 640 deletions(-) create mode 100644 src/main/java/org/gephi/graph/impl/AttributesImpl.java diff --git a/src/main/java/org/gephi/graph/api/Element.java b/src/main/java/org/gephi/graph/api/Element.java index bdd7a9c5..fdb5002b 100644 --- a/src/main/java/org/gephi/graph/api/Element.java +++ b/src/main/java/org/gephi/graph/api/Element.java @@ -109,7 +109,7 @@ public interface Element extends ElementProperties { /** * Returns an iterable over all the keys and values over time for the given - * column. + * (dynamic) column. * * @param column column * @return time attribute iterable diff --git a/src/main/java/org/gephi/graph/impl/AttributesImpl.java b/src/main/java/org/gephi/graph/impl/AttributesImpl.java new file mode 100644 index 00000000..5d474b86 --- /dev/null +++ b/src/main/java/org/gephi/graph/impl/AttributesImpl.java @@ -0,0 +1,256 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; + +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import org.gephi.graph.api.Column; +import org.gephi.graph.api.Estimator; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.types.IntervalMap; +import org.gephi.graph.api.types.IntervalSet; +import org.gephi.graph.api.types.TimeMap; +import org.gephi.graph.api.types.TimeSet; +import org.gephi.graph.api.types.TimestampMap; +import org.gephi.graph.api.types.TimestampSet; + +public class AttributesImpl { + + // Attributes + protected Object[] attributes; + + public AttributesImpl(ColumnStore columnStore) { + if (columnStore != null) { + int length = columnStore.length; + final ColumnImpl[] cols = columnStore.columns; + attributes = new Object[length]; + for (int i = 0; i < length; i++) { + Column c = cols[i]; + if (c != null && !c.isProperty()) { + attributes[i] = c.getDefaultValue(); + } + } + } else { + attributes = new Object[GraphStoreConfiguration.EDGE_DEFAULT_COLUMNS]; + } + } + + public Object getId() { + return attributes[GraphStoreConfiguration.ELEMENT_ID_INDEX]; + } + + public void setId(Object id) { + attributes[GraphStoreConfiguration.ELEMENT_ID_INDEX] = id; + } + + public String getLabel() { + if (GraphStoreConfiguration.ENABLE_ELEMENT_LABEL && attributes.length > GraphStoreConfiguration.ELEMENT_LABEL_INDEX) { + return (String) attributes[GraphStoreConfiguration.ELEMENT_LABEL_INDEX]; + } + return null; + } + + public Object getAttribute(Column column) { + return getAttribute(column.getIndex()); + } + + public Object getAttribute(int index) { + Object res = null; + synchronized (this) { + if (index < attributes.length) { + res = attributes[index]; + } + } + + return res; + } + + protected Object getAttribute(Column column, Object timeObject, Estimator estimator) { + int index = column.getIndex(); + synchronized (this) { + TimeMap dynamicValue = null; + if (index < attributes.length) { + dynamicValue = (TimeMap) attributes[index]; + } + if (dynamicValue != null && !dynamicValue.isEmpty()) { + if (estimator == null) { + return dynamicValue.get(timeObject, column.getDefaultValue()); + } else { + return dynamicValue.get((Interval) timeObject, estimator); + } + } + } + return null; + } + + protected Object removeTimeAttribute(Column column, Object timeObject) { + int index = column.getIndex(); + Object oldValue = null; + boolean res = false; + synchronized (this) { + TimeMap dynamicValue = (TimeMap) attributes[index]; + if (dynamicValue != null) { + oldValue = dynamicValue.get(timeObject, null); + + res = dynamicValue.remove(timeObject); + } + } + return oldValue; + } + + protected Object setAttribute(Column column, Object value) { + int index = column.getIndex(); + return setAttribute(index, value); + } + + public Object setAttribute(int index, Object value) { + Object oldValue = null; + if (index >= attributes.length) { + Object[] newArray = new Object[index + 1]; + System.arraycopy(attributes, 0, newArray, 0, attributes.length); + attributes = newArray; + } else { + oldValue = attributes[index]; + } + attributes[index] = value; + return oldValue; + } + + private Object ensureSize(int index) { + if (index >= attributes.length) { + Object[] newArray = new Object[index + 1]; + System.arraycopy(attributes, 0, newArray, 0, attributes.length); + attributes = newArray; + } else { + return attributes[index]; + } + return null; + } + + protected Object setAttribute(Column column, Object value, Object timeObject) { + int index = column.getIndex(); + Object oldValue = null; + synchronized (this) { + oldValue = ensureSize(index); + TimeMap dynamicValue; + if (oldValue == null) { + try { + attributes[index] = dynamicValue = (TimeMap) column.getTypeClass().getDeclaredConstructor() + .newInstance(); + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException + | InvocationTargetException ex) { + throw new RuntimeException(ex); + } + } else { + dynamicValue = (TimeMap) oldValue; + } + + dynamicValue.put(timeObject, value); + return dynamicValue; + } + } + + protected boolean addTime(Object timeObject) { + boolean res; + synchronized (this) { + TimeSet timeSet = getTimeSet(); + if (timeSet == null) { + if (timeObject instanceof Interval) { + timeSet = new IntervalSet(); + } else { + timeSet = new TimestampSet(); + } + int index = GraphStoreConfiguration.ELEMENT_TIMESET_INDEX; + if (index >= attributes.length) { + Object[] newArray = new Object[index + 1]; + System.arraycopy(attributes, 0, newArray, 0, attributes.length); + attributes = newArray; + } + attributes[index] = timeSet; + } + res = timeSet.add(timeObject); + } + return res; + } + + protected boolean removeTime(Object timeObject) { + boolean res = false; + synchronized (this) { + TimeSet timeSet = getTimeSet(); + if (timeSet != null) { + res = timeSet.remove(timeObject); + } + } + + return res; + } + + protected TimeSet getTimeSet() { + if (GraphStoreConfiguration.ENABLE_ELEMENT_TIME_SET && GraphStoreConfiguration.ELEMENT_TIMESET_INDEX < attributes.length) { + return (TimeSet) attributes[GraphStoreConfiguration.ELEMENT_TIMESET_INDEX]; + } + return null; + } + + protected boolean hasTime(Object timeObject) { + synchronized (this) { + TimeSet timeSet = getTimeSet(); + if (timeSet != null) { + return timeSet.contains(timeObject); + } + } + return false; + } + + protected Iterable getAttributes(Column column) { + int index = column.getIndex(); + TimeMap dynamicValue = null; + synchronized (this) { + if (index < attributes.length) { + dynamicValue = (TimeMap) attributes[index]; + } + if (dynamicValue != null) { + Object[] values = dynamicValue.toValuesArray(); + if (dynamicValue instanceof TimestampMap) { + return new TimeAttributeIterable(((TimestampMap) dynamicValue).getTimestamps(), values); + } else if (dynamicValue instanceof IntervalMap) { + return new TimeAttributeIterable(((IntervalMap) dynamicValue).toKeysArray(), values); + } + } + + } + return TimeAttributeIterable.EMPTY_ITERABLE; + } + + protected Object getTimeSetArray() { + synchronized (this) { + TimeSet timeSet = getTimeSet(); + if (timeSet != null) { + return timeSet.toPrimitiveArray(); + } + } + return null; + } + + public Object[] getBackingArray() { + return attributes; + } + + // Used by serialization + protected void setBackingArray(Object[] attributes) { + this.attributes = attributes; + } +} diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index ad512d55..c81d377b 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -126,7 +126,15 @@ public void addColumn(final Column column) { if (indexStore != null) { indexStore.addColumn(columnImpl); } + updateConfiguration(column); + + // Index attributes + if (graphStore != null && columnImpl.table != null) { + for (Element e : graphStore.getElements(columnImpl.table)) { + e.setAttribute(column, column.getDefaultValue()); + } + } } else { throw new IllegalArgumentException("The column " + column.getId() + " already exist"); } @@ -145,20 +153,8 @@ public void removeColumn(final Column column) { // Clean attributes if (graphStore != null && columnImpl.table != null) { - if (AttributeUtils.isNodeColumn(columnImpl)) { - for (Node n : graphStore.nodeStore) { - Object[] attributes = ((NodeImpl) n).attributes; - if (attributes.length > columnImpl.getIndex()) { - attributes[columnImpl.getIndex()] = null; - } - } - } else { - for (Edge e : graphStore.edgeStore) { - Object[] attributes = ((EdgeImpl) e).attributes; - if (attributes.length > columnImpl.getIndex()) { - attributes[columnImpl.getIndex()] = null; - } - } + for (Element e : graphStore.getElements(columnImpl.table)) { + ((ElementImpl) e).attributes.setAttribute(column, null); } } @@ -299,45 +295,6 @@ public Set getColumnKeys() { } } - public void clear() { - lock(); - try { - // Clean attributes - if (graphStore != null) { - List cols = toList(); - int[] indices = new int[cols.size()]; - for (int i = 0; i < indices.length; i++) { - indices[i] = cols.get(i).getIndex(); - } - if (graphStore.nodeTable.store == this) { - for (Node n : graphStore.nodeStore) { - Object[] atts = ((NodeImpl) n).attributes; - for (int i = 0; i < indices.length; i++) { - atts[indices[i]] = null; - } - } - } else { - for (Edge e : graphStore.edgeStore) { - Object[] atts = ((EdgeImpl) e).attributes; - for (int i = 0; i < indices.length; i++) { - atts[indices[i]] = null; - } - } - } - } - - garbageQueue.clear(); - idMap.clear(); - length = 0; - Arrays.fill(columns, null); - if (indexStore != null) { - indexStore.clear(); - } - } finally { - unlock(); - } - } - public int size() { return length - garbageQueue.size(); } diff --git a/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/src/main/java/org/gephi/graph/impl/EdgeImpl.java index 5f95838b..94a85fd6 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -57,10 +57,8 @@ public EdgeImpl(Object id, GraphStore graphStore, NodeImpl source, NodeImpl targ this.flags = (byte) (directed ? 1 : 0); this.type = type; this.properties = GraphStoreConfiguration.ENABLE_EDGE_PROPERTIES ? new EdgePropertiesImpl() : null; - this.attributes = new Object[GraphStoreConfiguration.EDGE_WEIGHT_INDEX + 1]; - this.attributes[GraphStoreConfiguration.ELEMENT_ID_INDEX] = id; if (graphStore == null || graphStore.configuration.getEdgeWeightType().equals(Double.class)) { - this.attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX] = weight; + this.attributes.setAttribute(GraphStoreConfiguration.EDGE_WEIGHT_INDEX, weight); } } @@ -81,7 +79,7 @@ public NodeImpl getTarget() { @Override public double getWeight() { synchronized (this) { - Object weightObject = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; + Object weightObject = attributes.getAttribute(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); if (weightObject instanceof Double) { return (Double) weightObject; } else { @@ -97,134 +95,48 @@ public boolean hasDynamicWeight() { @Override public void setWeight(double weight, double timestamp) { - checkTimeRepresentationTimestamp(); - setTimeWeight(weight, timestamp); + checkWeightDynamicType(); + setAttribute(graphStore.graphModel.defaultColumns.edgeWeight(), weight, timestamp); } @Override public void setWeight(double weight, Interval interval) { - checkTimeRepresentationInterval(); - setTimeWeight(weight, interval); - } - - private void setTimeWeight(double weight, Object timeObject) { checkWeightDynamicType(); - - boolean res; - synchronized (this) { - Object oldValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - TimeMap dynamicValue = null; - if (oldValue == null) { - try { - attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX] = dynamicValue = (TimeMap) graphStore.configuration - .getEdgeWeightType().newInstance(); - } catch (InstantiationException | IllegalAccessException ex) { - throw new RuntimeException(ex); - } - } else { - dynamicValue = (TimeMap) oldValue; - } - res = dynamicValue.put(timeObject, weight); - } - TimeIndexStore timeIndexStore = getTimeIndexStore(); - if (res && timeIndexStore != null && isValid()) { - timeIndexStore.add(timeObject); - } - ColumnStore columnStore = getColumnStore(); - if (res && columnStore != null && isValid()) { - Column column = columnStore.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); - ((ColumnImpl) column).incrementVersion(this); - } + setAttribute(graphStore.graphModel.defaultColumns.edgeWeight(), weight, interval); } @Override public double getWeight(double timestamp) { - synchronized (this) { - Object weightValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - if (weightValue instanceof Double) { - throw new IllegalStateException("The weight is static, call getWeight() instead"); - } - - TimeMap dynamicValue = (TimeMap) weightValue; - if (dynamicValue == null) { - return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } - - if (dynamicValue instanceof IntervalMap) { - return (Double) dynamicValue - .get(new Interval(timestamp, timestamp), DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); - } else { - return (Double) dynamicValue.get(timestamp, DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); - } - } + Column column = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + checkStaticWeight(column); + Double doubleVal = (Double) attributes.getAttribute(column, timestamp, null); + return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; } @Override public double getWeight(Interval interval) { - synchronized (this) { - Object weightValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - if (weightValue instanceof Double) { - throw new IllegalStateException("The weight is static, call getWeight() instead"); - } - - TimeMap dynamicValue = (TimeMap) weightValue; - if (dynamicValue == null) { - return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } - - Estimator estimator = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) - .getEstimator(); - if (estimator == null) { - estimator = GraphStoreConfiguration.DEFAULT_ESTIMATOR; - } - - Double doubleVal = (Double) dynamicValue.get(interval, estimator); - return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } + Column column = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + checkStaticWeight(column); + Double doubleVal = (Double) attributes.getAttribute(column, interval, getEstimator(column)); + return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; } @Override public double getWeight(GraphView view) { - synchronized (this) { - Object value = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - if (value instanceof TimeMap) { - Interval interval = view.getTimeInterval(); - checkViewExist((GraphView) view); - - TimeMap dynamicValue = (TimeMap) value; - Estimator estimator = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) - .getEstimator(); - if (estimator == null) { - estimator = GraphStoreConfiguration.DEFAULT_ESTIMATOR; - } - - Double doubleVal = (Double) dynamicValue.get(interval, estimator); - return doubleVal != null ? doubleVal : DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } else if (value == null) { - return DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; - } else { - // Must be double - return (Double) value; - } + checkViewExist(view); + Column column = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + if (column.isDynamicAttribute()) { + return getWeight(view.getTimeInterval()); + } else { + return (Double) attributes.getAttribute(column); } } @Override public Iterable getWeights() { - synchronized (this) { - Object weightValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - if (weightValue instanceof Double) { - throw new IllegalStateException("The weight is static, call getWeight() instead"); - } - TimeMap dynamicValue = (TimeMap) weightValue; - Object[] values = dynamicValue.toValuesArray(); - if (dynamicValue instanceof TimestampMap) { - return new TimeAttributeIterable(((TimestampMap) dynamicValue).getTimestamps(), values); - } else if (dynamicValue instanceof IntervalMap) { - return new TimeAttributeIterable(((IntervalMap) dynamicValue).toKeysArray(), values); - } - } - return TimeAttributeIterable.EMPTY_ITERABLE; + Column column = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + checkStaticWeight(column); + return attributes.getAttributes(column); } @Override @@ -246,20 +158,8 @@ public Object getTypeLabel() { public void setWeight(double weight) { checkWeightStaticType(); - final Object oldValue; - synchronized (this) { - oldValue = attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX]; - attributes[GraphStoreConfiguration.EDGE_WEIGHT_INDEX] = weight; - } - - ColumnStore columnStore = getColumnStore(); - if (columnStore != null && isValid()) { - Column column = columnStore.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); - ((ColumnImpl) column).incrementVersion(this); - if (column.isIndexed()) { - columnStore.indexStore.set(column, oldValue, weight, this); - } - } + Column column = getColumnStore().getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + setAttribute(column, weight); } public int getNextOutEdge() { @@ -435,6 +335,12 @@ final void checkWeightDynamicType() { } } + final void checkStaticWeight(Column column) { + if (!column.isDynamicAttribute()) { + throw new IllegalStateException("The weight is static, call getWeight() instead"); + } + } + protected static class EdgePropertiesImpl implements EdgeProperties { protected final TextPropertiesImpl textProperties; diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 4f21d5c9..780e09ac 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenCustomHashMap; @@ -38,7 +39,16 @@ public class EdgeStore implements Collection, EdgeIterable { protected final static int NULL_ID = -1; protected final static int NODE_BITS = 31; protected static final Iterator EMPTY_EDGE_ITERATOR = Collections.emptyIterator(); - + // Locking (optional) + protected final GraphLockImpl lock; + // Version + protected final GraphVersion version; + // Types counting (optional) + protected final EdgeTypeStore edgeTypeStore; + // View store + protected final GraphViewStore viewStore; + // Spatial index + protected final SpatialIndexImpl spatialIndex; // Data protected int size; protected int garbageSize; @@ -48,22 +58,10 @@ public class EdgeStore implements Collection, EdgeIterable { protected EdgeBlock currentBlock; protected Object2IntOpenHashMap dictionary; protected Long2ObjectOpenCustomHashMap[] longDictionary; - // Stats protected int undirectedSize; protected int mutualEdgesSize; protected int[] mutualEdgesTypeSize; - // Locking (optional) - protected final GraphLockImpl lock; - // Version - protected final GraphVersion version; - // Types counting (optional) - protected final EdgeTypeStore edgeTypeStore; - // View store - protected final GraphViewStore viewStore; - - // Spatial index - protected final SpatialIndexImpl spatialIndex; public EdgeStore() { initStore(); @@ -83,6 +81,18 @@ public EdgeStore(final EdgeTypeStore edgeTypeStore, final SpatialIndexImpl spati this.spatialIndex = spatialIndex; } + protected static long getLongId(NodeImpl source, NodeImpl target, boolean directed) { + if (directed) { + long edgeId = ((long) source.storeId) << NODE_BITS; + edgeId = edgeId | (long) (target.storeId); + return edgeId; + } else { + long edgeId = ((long) (source.storeId > target.storeId ? source.storeId : target.storeId)) << NODE_BITS; + edgeId = edgeId | (long) (source.storeId > target.storeId ? target.storeId : source.storeId); + return edgeId; + } + } + private void initStore() { this.size = 0; this.garbageSize = 0; @@ -655,7 +665,7 @@ public boolean remove(final Object o) { viewStore.removeEdge(edge); } - edge.clearAttributes(); + edge.destroyAttributes(); int storeIndex = id / GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE; EdgeBlock block = blocks[storeIndex]; @@ -1156,18 +1166,6 @@ int maxStoreId() { return currentBlock.offset + currentBlock.nodeLength; } - protected static long getLongId(NodeImpl source, NodeImpl target, boolean directed) { - if (directed) { - long edgeId = ((long) source.storeId) << NODE_BITS; - edgeId = edgeId | (long) (target.storeId); - return edgeId; - } else { - long edgeId = ((long) (source.storeId > target.storeId ? source.storeId : target.storeId)) << NODE_BITS; - edgeId = edgeId | (long) (source.storeId > target.storeId ? target.storeId : source.storeId); - return edgeId; - } - } - protected static class EdgeBlock { protected final int offset; @@ -1222,6 +1220,19 @@ public void clear() { } } + private static class DictionaryHashStrategy implements LongHash.Strategy { + + @Override + public int hashCode(long l) { + return (int) (l ^ (l >>> 32)); + } + + @Override + public boolean equals(long l1, long l2) { + return l1 == l2; + } + } + protected class EdgeStoreIterator implements Iterator { protected int blockIndex; @@ -1863,17 +1874,4 @@ public void remove() { EdgeStore.this.remove(pointer); } } - - private static class DictionaryHashStrategy implements LongHash.Strategy { - - @Override - public int hashCode(long l) { - return (int) (l ^ (l >>> 32)); - } - - @Override - public boolean equals(long l1, long l2) { - return l1 == l2; - } - } } diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index 412d4034..367999ff 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -13,30 +13,19 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; -import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Map; import java.util.Set; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnIterable; -import org.gephi.graph.api.Estimator; -import org.gephi.graph.api.Interval; -import org.gephi.graph.api.types.TimestampBooleanMap; -import org.gephi.graph.api.types.TimestampByteMap; -import org.gephi.graph.api.types.TimestampCharMap; -import org.gephi.graph.api.types.TimestampDoubleMap; -import org.gephi.graph.api.types.TimestampFloatMap; -import org.gephi.graph.api.types.TimestampIntegerMap; -import org.gephi.graph.api.types.TimestampLongMap; -import org.gephi.graph.api.types.TimestampSet; -import org.gephi.graph.api.types.TimestampShortMap; -import org.gephi.graph.api.types.TimestampStringMap; -import org.gephi.graph.api.types.TimestampMap; import org.gephi.graph.api.Element; +import org.gephi.graph.api.Estimator; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalBooleanMap; import org.gephi.graph.api.types.IntervalByteMap; @@ -46,24 +35,35 @@ import org.gephi.graph.api.types.IntervalIntegerMap; import org.gephi.graph.api.types.IntervalLongMap; import org.gephi.graph.api.types.IntervalMap; -import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.IntervalShortMap; import org.gephi.graph.api.types.IntervalStringMap; import org.gephi.graph.api.types.TimeMap; import org.gephi.graph.api.types.TimeSet; +import org.gephi.graph.api.types.TimestampBooleanMap; +import org.gephi.graph.api.types.TimestampByteMap; +import org.gephi.graph.api.types.TimestampCharMap; +import org.gephi.graph.api.types.TimestampDoubleMap; +import org.gephi.graph.api.types.TimestampFloatMap; +import org.gephi.graph.api.types.TimestampIntegerMap; +import org.gephi.graph.api.types.TimestampLongMap; +import org.gephi.graph.api.types.TimestampMap; +import org.gephi.graph.api.types.TimestampShortMap; +import org.gephi.graph.api.types.TimestampStringMap; public abstract class ElementImpl implements Element { // Reference to store protected final GraphStore graphStore; // Attributes - protected Object[] attributes; + protected final AttributesImpl attributes; public ElementImpl(Object id, GraphStore graphStore) { if (id == null) { throw new NullPointerException(); } this.graphStore = graphStore; + this.attributes = new AttributesImpl(getColumnStore()); + this.attributes.setId(id); } abstract ColumnStore getColumnStore(); @@ -74,15 +74,19 @@ public ElementImpl(Object id, GraphStore graphStore) { @Override public Object getId() { - return attributes[GraphStoreConfiguration.ELEMENT_ID_INDEX]; + return attributes.getId(); } @Override public String getLabel() { - if (GraphStoreConfiguration.ENABLE_ELEMENT_LABEL && attributes.length > GraphStoreConfiguration.ELEMENT_LABEL_INDEX) { - return (String) attributes[GraphStoreConfiguration.ELEMENT_LABEL_INDEX]; + return attributes.getLabel(); + } + + @Override + public void setLabel(String label) { + if (GraphStoreConfiguration.ENABLE_ELEMENT_LABEL) { + setAttribute(getColumnStore().getColumnByIndex(GraphStoreConfiguration.ELEMENT_LABEL_INDEX), label); } - return null; } @Override @@ -94,18 +98,7 @@ public Object getAttribute(String key) { public Object getAttribute(Column column) { checkColumn(column); - int index = column.getIndex(); - Object res = null; - synchronized (this) { - if (index < attributes.length) { - res = attributes[index]; - } - } - - if (res == null) { - return column.getDefaultValue(); - } - return res; + return attributes.getAttribute(column); } @Override @@ -117,7 +110,9 @@ public Object getAttribute(String key, double timestamp) { public Object getAttribute(Column column, double timestamp) { checkTimeRepresentationTimestamp(); checkDouble(timestamp); - return getTimeAttribute(column, timestamp); + checkColumn(column); + checkColumnDynamic(column); + return attributes.getAttribute(column, timestamp, null); } @Override @@ -128,24 +123,9 @@ public Object getAttribute(String key, Interval interval) { @Override public Object getAttribute(Column column, Interval interval) { checkTimeRepresentationInterval(); - return getTimeAttribute(column, interval); - } - - private Object getTimeAttribute(Column column, Object timeObject) { checkColumn(column); checkColumnDynamic(column); - - int index = column.getIndex(); - synchronized (this) { - TimeMap dynamicValue = null; - if (index < attributes.length) { - dynamicValue = (TimeMap) attributes[index]; - } - if (dynamicValue != null) { - return dynamicValue.get(timeObject, column.getDefaultValue()); - } - } - return null; + return attributes.getAttribute(column, interval, null); } @Override @@ -162,29 +142,13 @@ public Object getAttribute(Column column, GraphView view) { } else { Interval interval = view.getTimeInterval(); checkViewExist(view); - - int index = column.getIndex(); - synchronized (this) { - TimeMap dynamicValue = null; - if (index < attributes.length) { - dynamicValue = (TimeMap) attributes[index]; - } - if (dynamicValue != null && !dynamicValue.isEmpty()) { - Estimator estimator = column.getEstimator(); - if (estimator == null) { - estimator = GraphStoreConfiguration.DEFAULT_ESTIMATOR; - } - return dynamicValue.get(interval, estimator); - } - } + return attributes.getAttribute(column, interval, getEstimator(column)); } - - return null; } @Override public Object[] getAttributes() { - return attributes; + return attributes.getBackingArray(); } @Override @@ -207,37 +171,9 @@ public Object removeAttribute(Column column) { checkColumn(column); checkReadOnlyColumn(column); - int index = column.getIndex(); - Object oldValue = null; - synchronized (this) { - if (index >= attributes.length) { - Object[] newArray = new Object[index + 1]; - System.arraycopy(attributes, 0, newArray, 0, attributes.length); - attributes = newArray; - } else { - oldValue = attributes[index]; - } - - attributes[index] = null; - } + Object oldValue = attributes.setAttribute(column, column.getDefaultValue()); + updateIndex(column, oldValue, column.getDefaultValue()); - if (isValid()) { - ColumnStore columnStore = getColumnStore(); - ColumnImpl columnImpl = (ColumnImpl) column; - if (columnImpl.isDynamic() && oldValue != null) { - TimeIndexStore timeIndexStore = getTimeIndexStore(); - if (timeIndexStore != null) { - if (TimeMap.class.isAssignableFrom(columnImpl.getTypeClass())) { - timeIndexStore.remove((TimeMap) oldValue); - } else if (TimeSet.class.isAssignableFrom(columnImpl.getTypeClass())) { - timeIndexStore.remove((TimeSet) oldValue); - } - } - } else if (column.isIndexed() && columnStore != null && isValid()) { - columnStore.indexStore.set(column, oldValue, column.getDefaultValue(), this); - } - columnImpl.incrementVersion(this); - } return oldValue; } @@ -269,19 +205,10 @@ private Object removeTimeAttribute(Column column, Object timeObject) { checkColumnDynamic(column); checkReadOnlyColumn(column); - int index = column.getIndex(); - Object oldValue = null; - boolean res = false; - synchronized (this) { - TimeMap dynamicValue = (TimeMap) attributes[index]; - if (dynamicValue != null) { - oldValue = dynamicValue.get(timeObject, null); + Object oldValue = attributes.removeTimeAttribute(column, timeObject); - res = dynamicValue.remove(timeObject); - } - } - - if (res && isValid()) { + // TODO + if (oldValue != null && isValid()) { TimeIndexStore timeIndexStore = getTimeIndexStore(); if (timeIndexStore != null) { timeIndexStore.remove(timeObject); @@ -291,26 +218,6 @@ private Object removeTimeAttribute(Column column, Object timeObject) { return oldValue; } - @Override - public void setLabel(String label) { - if (GraphStoreConfiguration.ENABLE_ELEMENT_LABEL) { - int index = GraphStoreConfiguration.ELEMENT_LABEL_INDEX; - synchronized (this) { - if (index >= attributes.length) { - Object[] newArray = new Object[index + 1]; - System.arraycopy(attributes, 0, newArray, 0, attributes.length); - attributes = newArray; - } - attributes[index] = label; - } - ColumnStore columnStore = getColumnStore(); - if (columnStore != null && isValid()) { - Column col = columnStore.getColumnByIndex(index); - ((ColumnImpl) col).incrementVersion(this); - } - } - } - @Override public void setAttribute(String key, Object value) { setAttribute(checkColumnExists(key), value); @@ -324,49 +231,8 @@ public void setAttribute(Column column, Object value) { value = AttributeUtils.standardizeValue(value); checkType(column, value); - int index = column.getIndex(); - ColumnStore columnStore = getColumnStore(); - Object oldValue = null; - - synchronized (this) { - if (index >= attributes.length) { - Object[] newArray = new Object[index + 1]; - System.arraycopy(attributes, 0, newArray, 0, attributes.length); - attributes = newArray; - } else { - oldValue = attributes[index]; - } - - if (column.isDynamic() && isValid()) { - TimeIndexStore timeIndexStore = getTimeIndexStore(); - if (timeIndexStore != null) { - if (TimeMap.class.isAssignableFrom(column.getTypeClass())) { - if (oldValue != null && oldValue instanceof TimeMap) { - timeIndexStore.remove((TimeMap) oldValue); - } - if (value != null) { - timeIndexStore.add((TimeMap) value); - } - } else if (TimeSet.class.isAssignableFrom(column.getTypeClass()) && column - .getIndex() == GraphStoreConfiguration.ELEMENT_TIMESET_INDEX) { - if (oldValue != null) { - timeIndexStore.remove((TimeSet) oldValue); - } - if (value != null) { - timeIndexStore.add((TimeSet) value); - } - } - } - } - - if (column.isIndexed() && columnStore != null && isValid()) { - value = columnStore.indexStore.set(column, oldValue, value, this); - } - attributes[index] = value; - } - if (isValid()) { - ((ColumnImpl) column).incrementVersion(this); - } + Object oldValue = attributes.setAttribute(column, value); + updateIndex(column, oldValue, value); } @Override @@ -398,47 +264,47 @@ private void setTimeAttribute(Column column, Object value, Object timeObject) { checkReadOnlyColumn(column); checkType(column, value); - int index = column.getIndex(); - ColumnStore columnStore = getColumnStore(); - Object oldValue = null; - boolean res; - synchronized (this) { - if (index >= attributes.length) { - Object[] newArray = new Object[index + 1]; - System.arraycopy(attributes, 0, newArray, 0, attributes.length); - attributes = newArray; - } else { - oldValue = attributes[index]; - } - - TimeMap dynamicValue; - if (oldValue == null) { - try { - attributes[index] = dynamicValue = (TimeMap) column.getTypeClass().getDeclaredConstructor() - .newInstance(); - } catch (InstantiationException | IllegalAccessException | NoSuchMethodException - | InvocationTargetException ex) { - throw new RuntimeException(ex); - } - } else { - dynamicValue = (TimeMap) oldValue; - } - - res = dynamicValue.put(timeObject, value); + Object newValue = attributes.setAttribute(column, value, timeObject); + updateIndex(column, null, newValue); + } - if (res && isValid()) { + private void updateIndex(Column column, Object oldValue, Object newValue) { + // Update index + if (isValid()) { + ColumnStore columnStore = getColumnStore(); + ColumnImpl columnImpl = (ColumnImpl) column; + if (columnImpl.isDynamic()) { TimeIndexStore timeIndexStore = getTimeIndexStore(); if (timeIndexStore != null) { - timeIndexStore.add(timeObject); - } - - if (column.isIndexed() && columnStore != null) { - columnStore.indexStore.set(column, oldValue, dynamicValue, this); + if (TimeMap.class.isAssignableFrom(columnImpl.getTypeClass())) { + if (oldValue instanceof TimeMap) { + timeIndexStore.remove((TimeMap) oldValue); + } else if (oldValue != null) { + timeIndexStore.remove(oldValue, this); + } + if (newValue instanceof TimeMap) { + timeIndexStore.add((TimeMap) newValue); + } else if (newValue != null) { + timeIndexStore.add(newValue, this); + } + } else if (TimeSet.class.isAssignableFrom(columnImpl.getTypeClass())) { + if (oldValue instanceof TimeSet) { + timeIndexStore.remove((TimeSet) oldValue); + } else if (oldValue != null) { + timeIndexStore.remove(oldValue, this); + } + if (newValue instanceof TimeSet) { + timeIndexStore.add((TimeSet) newValue); + } else if (newValue != null) { + timeIndexStore.add(newValue, this); + } + } } } - } - if (isValid()) { - ((ColumnImpl) column).incrementVersion(this); + if (columnStore != null) { + columnStore.indexStore.set(column, oldValue, newValue, this); + } + columnImpl.incrementVersion(this); } } @@ -458,44 +324,11 @@ public boolean addInterval(Interval interval) { private boolean addTime(Object timeObject) { checkEnabledTimeSet(); - boolean res; - synchronized (this) { - TimeSet timeSet = getTimeSet(); - if (timeSet == null) { - TimeRepresentation timeRepresentation = getTimeRepresentation(); - switch (timeRepresentation) { - case INTERVAL: - timeSet = new IntervalSet(); - break; - case TIMESTAMP: - timeSet = new TimestampSet(); - break; - default: - throw new RuntimeException("Unrecognized time representation"); - } - int index = GraphStoreConfiguration.ELEMENT_TIMESET_INDEX; - if (index >= attributes.length) { - Object[] newArray = new Object[index + 1]; - System.arraycopy(attributes, 0, newArray, 0, attributes.length); - attributes = newArray; - } - attributes[index] = timeSet; - } - res = timeSet.add(timeObject); + boolean res = attributes.addTime(timeObject); + if (res) { + Column column = getColumnStore().getColumnByIndex(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); + updateIndex(column, null, timeObject); } - - if (res && isValid()) { - TimeIndexStore timeIndexStore = getTimeIndexStore(); - if (timeIndexStore != null) { - timeIndexStore.add(timeObject, this); - } - ColumnStore columnStore = getColumnStore(); - if (columnStore != null) { - Column column = columnStore.getColumnByIndex(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); - ((ColumnImpl) column).incrementVersion(this); - } - } - return res; } @@ -515,33 +348,19 @@ public boolean removeInterval(Interval interval) { private boolean removeTime(Object timeObject) { checkEnabledTimeSet(); - boolean res = false; - synchronized (this) { - TimeSet timeSet = getTimeSet(); - if (timeSet != null) { - res = timeSet.remove(timeObject); - } - } - - if (res && isValid()) { - TimeIndexStore timeIndexStore = getTimeIndexStore(); - if (timeIndexStore != null) { - timeIndexStore.remove(timeObject, this); - } - ColumnStore columnStore = getColumnStore(); - if (columnStore != null) { - Column column = columnStore.getColumnByIndex(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); - ((ColumnImpl) column).incrementVersion(this); - } + boolean res = attributes.removeTime(timeObject); + if (res) { + Column column = getColumnStore().getColumnByIndex(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); + updateIndex(column, timeObject, null); } - return res; } @Override public double[] getTimestamps() { checkTimeRepresentationTimestamp(); - Object res = getTimeSetArray(); + checkEnabledTimeSet(); + Object res = attributes.getTimeSetArray(); if (res == null) { return new double[0]; } @@ -551,7 +370,8 @@ public double[] getTimestamps() { @Override public Interval[] getIntervals() { checkTimeRepresentationInterval(); - Object res = getTimeSetArray(); + checkEnabledTimeSet(); + Object res = attributes.getTimeSetArray(); if (res == null) { return new Interval[0]; } @@ -560,7 +380,8 @@ public Interval[] getIntervals() { @Override public Interval getTimeBounds() { - TimeSet timeSet = getTimeSet(); + checkEnabledTimeSet(); + TimeSet timeSet = attributes.getTimeSet(); if (timeSet != null) { Double min = timeSet.getMinDouble(); Double max = timeSet.getMaxDouble(); @@ -571,40 +392,18 @@ public Interval getTimeBounds() { return null; } - private Object getTimeSetArray() { - checkEnabledTimeSet(); - - synchronized (this) { - TimeSet timeSet = getTimeSet(); - if (timeSet != null) { - return timeSet.toPrimitiveArray(); - } - } - return null; - } - @Override public boolean hasTimestamp(double timestamp) { checkTimeRepresentationTimestamp(); - return hasTime(timestamp); + checkEnabledTimeSet(); + return attributes.hasTime(timestamp); } @Override public boolean hasInterval(Interval interval) { checkTimeRepresentationInterval(); - return hasTime(interval); - } - - private boolean hasTime(Object timeObject) { checkEnabledTimeSet(); - - synchronized (this) { - TimeSet timeSet = getTimeSet(); - if (timeSet != null) { - return timeSet.contains(timeObject); - } - } - return false; + return attributes.hasTime(interval); } @Override @@ -612,32 +411,11 @@ public Iterable getAttributes(Column column) { checkColumn(column); checkColumnDynamic(column); - int index = column.getIndex(); - TimeMap dynamicValue = null; - synchronized (this) { - if (index < attributes.length) { - dynamicValue = (TimeMap) attributes[index]; - } - if (dynamicValue != null) { - Object[] values = dynamicValue.toValuesArray(); - if (dynamicValue instanceof TimestampMap) { - return new TimeAttributeIterable(((TimestampMap) dynamicValue).getTimestamps(), values); - } else if (dynamicValue instanceof IntervalMap) { - return new TimeAttributeIterable(((IntervalMap) dynamicValue).toKeysArray(), values); - } - } - - } - return TimeAttributeIterable.EMPTY_ITERABLE; - } - - private TimeSet getTimeSet() { - if (GraphStoreConfiguration.ENABLE_ELEMENT_TIME_SET && GraphStoreConfiguration.ELEMENT_TIMESET_INDEX < attributes.length) { - return (TimeSet) attributes[GraphStoreConfiguration.ELEMENT_TIMESET_INDEX]; - } - return null; + return attributes.getAttributes(column); } + // Called when elements are added + // TODO protected void indexAttributes() { synchronized (this) { ColumnStore columnStore = getColumnStore(); @@ -655,24 +433,31 @@ protected void indexAttributes() { @Override public void clearAttributes() { synchronized (this) { - if (isValid()) { - ColumnStore columnStore = getColumnStore(); - if (columnStore != null) { - columnStore.indexStore.clear(this); - } - TimeIndexStore timeIndexStore = getTimeIndexStore(); - if (timeIndexStore != null) { - timeIndexStore.clear(this); + ColumnStore columnStore = getColumnStore(); + if (columnStore != null) { + final int length = columnStore.length; + final ColumnImpl[] cols = columnStore.columns; + for (int i = 0; i < length; i++) { + Column c = cols[i]; + if (!c.isProperty() && !c.isReadOnly()) { + removeAttribute(c); + } } } - TimeSet timeSet = getTimeSet(); - if (timeSet != null) { - timeSet.clear(); + } + } + + protected void destroyAttributes() { + synchronized (this) { + ColumnStore columnStore = getColumnStore(); + if (columnStore != null) { + columnStore.indexStore.clear(this); } - Object[] newAttributes = new Object[GraphStoreConfiguration.ELEMENT_ID_INDEX + 1]; - newAttributes[GraphStoreConfiguration.ELEMENT_ID_INDEX] = attributes[GraphStoreConfiguration.ELEMENT_ID_INDEX]; - attributes = newAttributes; + TimeIndexStore timeIndexStore = getTimeIndexStore(); + if (timeIndexStore != null) { + timeIndexStore.clear(this); + } } } @@ -695,6 +480,14 @@ public boolean equals(Object obj) { return this.getId().equals(other.getId()); } + protected Estimator getEstimator(Column column) { + Estimator estimator = column.getEstimator(); + if (estimator == null) { + return GraphStoreConfiguration.DEFAULT_ESTIMATOR; + } + return estimator; + } + protected GraphStore getGraphStore() { return graphStore; } diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 5714c1ee..4d48dfea 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; import java.util.ArrayList; @@ -23,23 +24,25 @@ import java.util.Set; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Origin; -import org.gephi.graph.api.TimeFormat; -import org.gephi.graph.api.Interval; -import org.gephi.graph.api.types.TimestampSet; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; +import org.gephi.graph.api.ElementIterable; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; +import org.gephi.graph.api.Origin; import org.gephi.graph.api.Subgraph; -import org.joda.time.DateTimeZone; +import org.gephi.graph.api.Table; +import org.gephi.graph.api.TimeFormat; import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalSet; +import org.gephi.graph.api.types.TimestampSet; +import org.joda.time.DateTimeZone; public class GraphStore implements DirectedGraph, DirectedSubgraph { @@ -240,6 +243,10 @@ public EdgeIterable getEdges() { return edgeStore; } + protected ElementIterable getElements(Table table) { + return table.isNodeTable() ? nodeStore : edgeStore; + } + @Override public EdgeIterable getEdges(int type) { return new EdgeIterableWrapper(edgeStore.iteratorType(type, false)); diff --git a/src/main/java/org/gephi/graph/impl/IndexStore.java b/src/main/java/org/gephi/graph/impl/IndexStore.java index e9bb6996..5d05541a 100644 --- a/src/main/java/org/gephi/graph/impl/IndexStore.java +++ b/src/main/java/org/gephi/graph/impl/IndexStore.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -137,8 +138,8 @@ public void clear(T element) { final ColumnImpl[] cols = columnStore.columns; for (int i = 0; i < length; i++) { Column c = cols[i]; - if (c != null && c.isIndexed() && elementImpl.attributes.length > c.getIndex()) { - Object value = elementImpl.attributes[c.getIndex()]; + if (c != null && c.isIndexed()) { + Object value = elementImpl.getAttribute(c); mainIndex.remove(c, value, element); for (Entry> entry : viewIndexes.entrySet()) { GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); @@ -160,16 +161,15 @@ public void index(T element) { ElementImpl elementImpl = (ElementImpl) element; lock(); try { - ensureAttributeArrayLength(elementImpl, columnStore.length); final int length = columnStore.length; final ColumnImpl[] cols = columnStore.columns; for (int i = 0; i < length; i++) { Column c = cols[i]; if (c != null && c.isIndexed()) { - Object value = elementImpl.attributes[c.getIndex()]; + Object value = elementImpl.getAttribute(c); value = mainIndex.put(c, value, element); - elementImpl.attributes[c.getIndex()] = value; + elementImpl.setAttribute(c, value); } } } finally { @@ -192,7 +192,6 @@ public void indexView(Graph graph) { if (iterator != null) { while (iterator.hasNext()) { ElementImpl element = (ElementImpl) iterator.next(); - ensureAttributeArrayLength(element, columnStore.length); final ColumnImpl[] cols = columnStore.columns; synchronized (element) { @@ -200,7 +199,7 @@ public void indexView(Graph graph) { for (int i = 0; i < length; i++) { Column c = cols[i]; if (c != null && c.isIndexed()) { - Object value = element.attributes[c.getIndex()]; + Object value = element.getAttribute(c); viewIndex.put(c, value, element); } } @@ -225,7 +224,7 @@ public void indexInView(T element, GraphView view) { Column c = cols[i]; if (c != null && c.isIndexed()) { synchronized (elementImpl) { - Object value = elementImpl.attributes[c.getIndex()]; + Object value = elementImpl.getAttribute(c); index.put(c, value, element); } } @@ -248,7 +247,7 @@ public void clearInView(T element, GraphView view) { Column c = cols[i]; if (c != null && c.isIndexed()) { synchronized (elementImpl) { - Object value = elementImpl.attributes[c.getIndex()]; + Object value = elementImpl.getAttribute(c); index.remove(c, value, element); } } @@ -283,18 +282,6 @@ public void clear() { } } - private void ensureAttributeArrayLength(ElementImpl element, int size) { - synchronized (element) { - final Object[] attributes = element.attributes; - if (size > attributes.length) { - Object[] newArray = new Object[size]; - System.arraycopy(attributes, 0, newArray, 0, attributes.length); - element.attributes = newArray; - } - } - - } - private void lock() { if (lock != null) { lock.lock(); diff --git a/src/main/java/org/gephi/graph/impl/NodeImpl.java b/src/main/java/org/gephi/graph/impl/NodeImpl.java index 32f3a8a9..b28a2c26 100644 --- a/src/main/java/org/gephi/graph/impl/NodeImpl.java +++ b/src/main/java/org/gephi/graph/impl/NodeImpl.java @@ -37,8 +37,6 @@ public NodeImpl(Object id, GraphStore graphStore) { super(id, graphStore); checkIdType(id); this.properties = GraphStoreConfiguration.ENABLE_NODE_PROPERTIES ? new NodePropertiesImpl() : null; - this.attributes = new Object[GraphStoreConfiguration.ELEMENT_ID_INDEX + 1]; - this.attributes[GraphStoreConfiguration.ELEMENT_ID_INDEX] = id; } public NodeImpl(Object id) { diff --git a/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java index 9a408885..841eb1a7 100644 --- a/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -38,6 +39,8 @@ public class NodeStore implements Collection, NodeIterable { protected final GraphLockImpl lock; // Version protected final GraphVersion version; + // View store + protected final GraphViewStore viewStore; // Data protected int size; protected int garbageSize; @@ -46,8 +49,6 @@ public class NodeStore implements Collection, NodeIterable { protected NodeBlock blocks[]; protected NodeBlock currentBlock; protected Object2IntOpenHashMap dictionary; - // View store - protected final GraphViewStore viewStore; public NodeStore() { initStore(); @@ -311,7 +312,7 @@ public boolean remove(final Object o) { spatialIndex.removeNode(node); } - node.clearAttributes(); + node.destroyAttributes(); incrementVersion(); diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 69487f88..ed801c66 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -350,7 +350,7 @@ public GraphStore deserializeGraphStore(DataInput is) throws IOException, ClassN private void serializeNode(DataOutput out, NodeImpl node) throws IOException { serialize(out, node.getId()); serialize(out, node.storeId); - serialize(out, node.attributes); + serialize(out, node.attributes.attributes); serialize(out, node.properties); } @@ -365,7 +365,7 @@ private void serializeEdge(DataOutput out, EdgeImpl edge) throws IOException { serialize(out, GraphStoreConfiguration.DEFAULT_EDGE_WEIGHT); } serialize(out, edge.isDirected()); - serialize(out, edge.attributes); + serialize(out, edge.attributes.attributes); serialize(out, edge.properties); } @@ -376,7 +376,7 @@ private NodeImpl deserializeNode(DataInput is) throws IOException, ClassNotFound NodePropertiesImpl properties = (NodePropertiesImpl) deserialize(is); NodeImpl node = (NodeImpl) model.store.factory.newNode(id); - node.attributes = attributes; + node.attributes.setBackingArray(attributes); if (node.properties != null) { node.setNodeProperties(properties); } @@ -408,7 +408,7 @@ private EdgeImpl deserializeEdge(DataInput is) throws IOException, ClassNotFound NodeImpl target = model.store.nodeStore.get(targetNewId); EdgeImpl edge = (EdgeImpl) model.store.factory.newEdge(id, source, target, type, weight, directed); - edge.attributes = attributes; + edge.attributes.setBackingArray(attributes); if (edge.properties != null) { edge.setEdgeProperties(properties); } diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index a09b600a..7ace62a6 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -188,7 +188,7 @@ public boolean remove(Object o) { @Override public void clear() { - store.clear(); + throw new UnsupportedOperationException("This method from Collection isn't implemented"); } @Override diff --git a/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java b/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java index 96d399b9..8544b71b 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnStoreTest.java @@ -310,18 +310,6 @@ public void testGetColumnKeys() { Assert.assertEquals(store.getColumnKeys(), set); } - @Test - public void testClear() { - ColumnStore store = new ColumnStore(Node.class, false); - ColumnImpl col = new ColumnImpl("0", Integer.class, null, null, Origin.DATA, false, false); - - store.addColumn(col); - store.clear(); - - Assert.assertFalse(store.hasColumn("0")); - Assert.assertEquals(store.size(), 0); - } - @Test public void testGarbage() { ColumnStore store = new ColumnStore(Node.class, false); diff --git a/src/test/java/org/gephi/graph/impl/EdgeImplTest.java b/src/test/java/org/gephi/graph/impl/EdgeImplTest.java index 84d7254b..a4405b0d 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeImplTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeImplTest.java @@ -310,6 +310,18 @@ public void testGetWeightWithView() { Assert.assertEquals(e.getWeight(view), 20.0); } + @Test + public void testGetWeightWithViewStatic() { + Configuration config = new Configuration(); + config.setEdgeWeightType(Double.class); + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); + Edge e = graphStore.getEdge("0"); + e.setWeight(10.0); + GraphViewImpl view = graphStore.viewStore.createView(); + + Assert.assertEquals(e.getWeight(view), 10.0); + } + @Test public void testGetWeightsTimestamp() { Configuration config = new Configuration(); diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index d6757abe..88636c01 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -84,8 +84,8 @@ public void testSetAttributeColumn() { NodeImpl node = new NodeImpl("0", store); node.setAttribute(column, 1); - Assert.assertEquals(node.attributes.length, 1 + getElementPropertiesLength()); - Assert.assertEquals(node.attributes[getFirstNonPropertyIndex()], 1); + Assert.assertEquals(node.getAttributes().length, 1 + getElementPropertiesLength()); + Assert.assertEquals(node.getAttributes()[getFirstNonPropertyIndex()], 1); Assert.assertEquals(node.getAttribute(column), 1); } @@ -97,8 +97,8 @@ public void testSetAttributeString() { NodeImpl node = new NodeImpl("0", store); node.setAttribute("age", 1); - Assert.assertEquals(node.attributes.length, 1 + getElementPropertiesLength()); - Assert.assertEquals(node.attributes[getFirstNonPropertyIndex()], 1); + Assert.assertEquals(node.getAttributes().length, 1 + getElementPropertiesLength()); + Assert.assertEquals(node.getAttributes()[getFirstNonPropertyIndex()], 1); Assert.assertEquals(node.getAttribute(column), 1); } @@ -194,8 +194,8 @@ public void testSetAttributeTimestamp() { NodeImpl node = new NodeImpl("0", store); node.setAttribute(column, ti); - Assert.assertEquals(node.attributes.length, 1 + getElementPropertiesLength()); - Assert.assertEquals(node.attributes[getFirstNonPropertyIndex()], ti); + Assert.assertEquals(node.getAttributes().length, 1 + getElementPropertiesLength()); + Assert.assertEquals(node.getAttributes()[getFirstNonPropertyIndex()], ti); Assert.assertEquals(node.getAttribute(column), ti); } @@ -211,8 +211,8 @@ public void testSetAttributeInterval() { NodeImpl node = new NodeImpl("0", store); node.setAttribute(column, ti); - Assert.assertEquals(node.attributes.length, 1 + getElementPropertiesLength()); - Assert.assertEquals(node.attributes[getFirstNonPropertyIndex()], ti); + Assert.assertEquals(node.getAttributes().length, 1 + getElementPropertiesLength()); + Assert.assertEquals(node.getAttributes()[getFirstNonPropertyIndex()], ti); Assert.assertEquals(node.getAttribute(column), ti); } @@ -278,8 +278,8 @@ public void testSetAttributeNull() { NodeImpl node = new NodeImpl("0", store); node.setAttribute(column, null); - Assert.assertEquals(node.attributes.length, 1 + getElementPropertiesLength()); - Assert.assertNull(node.attributes[getFirstNonPropertyIndex()]); + Assert.assertEquals(node.getAttributes().length, 1 + getElementPropertiesLength()); + Assert.assertNull(node.getAttributes()[getFirstNonPropertyIndex()]); Assert.assertNull(node.getAttribute(column)); } @@ -292,8 +292,8 @@ public void testSetAttributeTimestampColumn() { node.setAttribute(column, 1, 2.0); node.setAttribute(column, 2, 1.0); - Assert.assertEquals(node.attributes.length, 1 + getElementPropertiesLength()); - Assert.assertEquals(node.attributes[getFirstNonPropertyIndex()].getClass(), TimestampIntegerMap.class); + Assert.assertEquals(node.getAttributes().length, 1 + getElementPropertiesLength()); + Assert.assertEquals(node.getAttributes()[getFirstNonPropertyIndex()].getClass(), TimestampIntegerMap.class); Assert.assertEquals(node.getAttribute(column, 2.0), 1); Assert.assertEquals(node.getAttribute(column, 1.0), 2); } @@ -307,8 +307,8 @@ public void testSetAttributeIntervalColumn() { node.setAttribute(column, 1, new Interval(3.0, 4.0)); node.setAttribute(column, 2, new Interval(1.0, 2.0)); - Assert.assertEquals(node.attributes.length, 1 + getElementPropertiesLength()); - Assert.assertEquals(node.attributes[getFirstNonPropertyIndex()].getClass(), IntervalIntegerMap.class); + Assert.assertEquals(node.getAttributes().length, 1 + getElementPropertiesLength()); + Assert.assertEquals(node.getAttributes()[getFirstNonPropertyIndex()].getClass(), IntervalIntegerMap.class); Assert.assertEquals(node.getAttribute(column, new Interval(3.0, 4.0)), 1); Assert.assertEquals(node.getAttribute(column, new Interval(1.0, 2.0)), 2); } @@ -580,11 +580,15 @@ public void testGetDefaultValue() { node.setAttribute(column, null); res = node.getAttribute(column.getId()); - Assert.assertEquals(res, defaultValue); + Assert.assertNull(res); node.setAttribute(column, 1); res = node.getAttribute(column.getId()); Assert.assertEquals(res, 1); + + node.removeAttribute(column); + res = node.getAttribute(column.getId()); + Assert.assertEquals(res, defaultValue); } @Test @@ -1128,6 +1132,35 @@ public void testGetTable() { } } + @Test + public void testRemoveColumn() { + GraphStore store = new GraphStore(); + Column column = generateBasicColumn(store); + + NodeImpl node = new NodeImpl("0", store); + node.setAttribute(column, 1); + store.addNode(node); + + int index = column.getIndex(); + column.getTable().removeColumn(column); + Assert.assertNull(node.getAttributes()[index]); + } + + @Test + public void testRemoveColumnDefaultValue() { + GraphStore store = new GraphStore(); + Column column = new ColumnImpl(store.nodeTable, "age", Integer.class, "Age", 25, Origin.DATA, true, false); + store.nodeTable.store.addColumn(column); + + NodeImpl node = new NodeImpl("0", store); + node.setAttribute(column, 1); + store.addNode(node); + + int index = column.getIndex(); + column.getTable().removeColumn(column); + Assert.assertNull(node.getAttributes()[index]); + } + // Utility private GraphStore getIntervalGraphStore() { Configuration config = new Configuration(); @@ -1138,42 +1171,44 @@ private GraphStore getIntervalGraphStore() { } private Column generateBasicColumn(GraphStore graphStore) { - graphStore.nodeTable.store - .addColumn(new ColumnImpl("age", Integer.class, "Age", null, Origin.DATA, true, false)); + graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "age", Integer.class, "Age", null, + Origin.DATA, true, false)); return graphStore.nodeTable.store.getColumn("age"); } private Column generateBasicBooleanColumn(GraphStore graphStore) { - graphStore.nodeTable.store - .addColumn(new ColumnImpl("visible", Boolean.class, "Visible", null, Origin.DATA, true, false)); + graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "visible", Boolean.class, "Visible", + null, Origin.DATA, true, false)); return graphStore.nodeTable.store.getColumn("visible"); } private Column generateBasicListColumn(GraphStore graphStore) { - graphStore.nodeTable.store - .addColumn(new ColumnImpl("list", List.class, "List", null, Origin.DATA, true, false)); + graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "list", List.class, "List", null, + Origin.DATA, true, false)); return graphStore.nodeTable.store.getColumn("list"); } private Column generateBasicSetColumn(GraphStore graphStore) { - graphStore.nodeTable.store.addColumn(new ColumnImpl("set", Set.class, "Set", null, Origin.DATA, true, false)); + graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "set", Set.class, "Set", null, + Origin.DATA, true, false)); return graphStore.nodeTable.store.getColumn("set"); } private Column generateBasicMapColumn(GraphStore graphStore) { - graphStore.nodeTable.store.addColumn(new ColumnImpl("map", Map.class, "Map", null, Origin.DATA, true, false)); + graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "map", Map.class, "Map", null, + Origin.DATA, true, false)); return graphStore.nodeTable.store.getColumn("map"); } private Column generateTimestampColumn(GraphStore graphStore) { - graphStore.nodeTable.store - .addColumn(new ColumnImpl("age", TimestampIntegerMap.class, "Age", null, Origin.DATA, false, false)); + graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "age", TimestampIntegerMap.class, + "Age", null, Origin.DATA, false, false)); return graphStore.nodeTable.store.getColumn("age"); } private Column generateIntervalColumn(GraphStore graphStore) { - graphStore.nodeTable.store - .addColumn(new ColumnImpl("age", IntervalIntegerMap.class, "Age", null, Origin.DATA, false, false)); + graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "age", IntervalIntegerMap.class, + "Age", null, Origin.DATA, false, false)); return graphStore.nodeTable.store.getColumn("age"); } diff --git a/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/src/test/java/org/gephi/graph/impl/GraphModelTest.java index 37b8b664..98d29285 100644 --- a/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -717,7 +717,6 @@ public void testNodeAttributesAddAndClearColumns() { n1.setAttribute(col1, "bar"); graphModel.getStore().addNode(n1); - ((TableImpl) table).store.clear(); Column col2 = table.addColumn("foo2", String.class); Assert.assertNull(n1.getAttribute(col2)); diff --git a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java index ec356790..17d8af87 100644 --- a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java @@ -13,16 +13,17 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; import java.util.ArrayList; import java.util.List; import org.gephi.graph.api.Column; import org.gephi.graph.api.Edge; -import org.gephi.graph.api.Origin; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Node; +import org.gephi.graph.api.Origin; import org.gephi.graph.api.Subgraph; import org.testng.Assert; import org.testng.annotations.Test; @@ -82,7 +83,7 @@ public void testIndexNodeNull() { NodeImpl n = new NodeImpl("0"); indexStore.index(n); - Assert.assertEquals(n.attributes.length, indexStore.columnStore.length); + Assert.assertEquals(n.getAttributes().length, indexStore.columnStore.length); } @Test @@ -267,7 +268,7 @@ public void testNodePropertyIndices() { Assert.assertNotNull(idIndex); Assert.assertNotNull(labelIndex); - NodeImpl n1 = new NodeImpl("0"); + Node n1 = graphStore.factory.newNode("0"); graphStore.addNode(n1); Assert.assertEquals(mainIndex.count(idCol, "0"), 1); @@ -494,9 +495,9 @@ public void testClearElementWithView() { n1.clearAttributes(); - Assert.assertEquals(index.countElements(column), 0); - Assert.assertEquals(index.countValues(column), 0); - Assert.assertEquals(index.count(column, "bar"), 0); + Assert.assertEquals(index.countElements(column), 1); + Assert.assertEquals(index.countValues(column), 1); + Assert.assertEquals(index.count(column, null), 1); } @Test @@ -521,6 +522,38 @@ public void testClearInView() { Assert.assertEquals(index.count(column, "bar"), 0); } + @Test + public void testNullAddColumn() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + + ColumnImpl column = new ColumnImpl(graphStore.nodeTable, "foo", String.class, "Foo", null, Origin.DATA, true, + false); + graphStore.nodeTable.store.addColumn(column); + + IndexStore indexStore = graphStore.nodeTable.store.indexStore; + IndexImpl index = indexStore.mainIndex; + + Assert.assertEquals(index.countElements(column), graphStore.getNodeCount()); + Assert.assertEquals(index.countValues(column), 1); + Assert.assertEquals(index.count(column, null), graphStore.getNodeCount()); + } + + @Test + public void testDefaultAddColumn() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + + ColumnImpl column = new ColumnImpl(graphStore.nodeTable, "foo", String.class, "Foo", "bar", Origin.DATA, true, + false); + graphStore.nodeTable.store.addColumn(column); + + IndexStore indexStore = graphStore.nodeTable.store.indexStore; + IndexImpl index = indexStore.mainIndex; + + Assert.assertEquals(index.countElements(column), graphStore.getNodeCount()); + Assert.assertEquals(index.countValues(column), 1); + Assert.assertEquals(index.count(column, "bar"), graphStore.getNodeCount()); + } + // UTILITY private NodeImpl[] generateNodesWithUniqueAttributes(ColumnStore columnStore) { int count = 100; diff --git a/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java index 701d8c2c..a1a6c11d 100644 --- a/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.impl; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; @@ -609,7 +610,7 @@ public void testClearElementWithView() { Graph graph = graphStore.viewStore.getGraph(view); view.fill(); TimeIndexImpl index = store.createViewIndex(graph); - n1.clearAttributes(); + n1.destroyAttributes(); Assert.assertFalse(index.hasElements()); } diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 08a31a34..9607d0af 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -198,7 +198,7 @@ public void testNode() throws IOException, ClassNotFoundException { ser = new Serialization(graphModel); NodeImpl l = (NodeImpl) ser.deserialize(buf); Assert.assertTrue(node.equals(l)); - Assert.assertTrue(Arrays.deepEquals(l.attributes, node.attributes)); + Assert.assertTrue(Arrays.deepEquals(l.getAttributes(), node.getAttributes())); } @Test diff --git a/src/test/java/org/gephi/graph/impl/TableImplTest.java b/src/test/java/org/gephi/graph/impl/TableImplTest.java index a385d9b4..9ed0422b 100644 --- a/src/test/java/org/gephi/graph/impl/TableImplTest.java +++ b/src/test/java/org/gephi/graph/impl/TableImplTest.java @@ -251,14 +251,6 @@ public void testRemoveColumnString() { Assert.assertFalse(table.hasColumn("Id")); } - @Test - public void testClear() { - TableImpl table = new TableImpl<>(Node.class, false); - table.addColumn("Id", Integer.class); - table.clear(); - Assert.assertTrue(table.isEmpty()); - } - @Test public void testCountColumns() { TableImpl table = new TableImpl<>(Node.class, false); diff --git a/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java b/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java index c93a119e..a6f2e1c5 100644 --- a/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java @@ -622,7 +622,7 @@ public void testClearElementWithView() { Graph graph = graphStore.viewStore.getGraph(view); view.fill(); TimeIndexImpl index = store.createViewIndex(graph); - n1.clearAttributes(); + n1.destroyAttributes(); Assert.assertFalse(index.hasElements()); } From b0433fe089f6728ff5fee23afc8da6bf3ffa76fc Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 1 Apr 2022 19:41:23 +0200 Subject: [PATCH 079/271] Add ensureCapacity test --- .../org/gephi/graph/impl/ElementImplTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index 88636c01..2b5b9428 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -1161,6 +1161,21 @@ public void testRemoveColumnDefaultValue() { Assert.assertNull(node.getAttributes()[index]); } + @Test + public void testEnsureCapacity() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + Node n1 = graphStore.getNode("1"); + Assert.assertTrue(n1.getAttributes().length < 100); + for (int i = 0; i < 100; i++) { + Column col = new ColumnImpl(graphStore.nodeTable, "col" + i, Integer.class, "Age", null, Origin.DATA, true, + false); + graphStore.nodeTable.store.addColumn(col); + Assert.assertNull(n1.getAttribute(col)); + n1.setAttribute(col, i); + Assert.assertEquals(i, n1.getAttribute(col)); + } + } + // Utility private GraphStore getIntervalGraphStore() { Configuration config = new Configuration(); From 76126d3b18449602cb1474d6e03d64f24a89ecfa Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 1 Apr 2022 19:41:39 +0200 Subject: [PATCH 080/271] Set version to 0.6.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d4fd74ed..845338bf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.4-SNAPSHOT + 0.6.4 jar GraphStore From 876585695430e25f7999b851ec54ac011fe3c2da Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 1 Apr 2022 19:46:09 +0200 Subject: [PATCH 081/271] Increment version to 0.6.5-SNAPSHOT [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 845338bf..60bdd004 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.4 + 0.6.5-SNAPSHOT jar GraphStore From f58909d6ae5240331de6f43be6f6f0469e00f4e8 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 16 Apr 2022 09:21:17 +0200 Subject: [PATCH 082/271] Fix locking issues --- .../org/gephi/graph/impl/AttributesImpl.java | 16 +++++++++------- .../java/org/gephi/graph/impl/ColumnStore.java | 2 ++ .../org/gephi/graph/impl/GraphViewStore.java | 1 - 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/AttributesImpl.java b/src/main/java/org/gephi/graph/impl/AttributesImpl.java index 5d474b86..0f3b8d6c 100644 --- a/src/main/java/org/gephi/graph/impl/AttributesImpl.java +++ b/src/main/java/org/gephi/graph/impl/AttributesImpl.java @@ -118,14 +118,16 @@ protected Object setAttribute(Column column, Object value) { public Object setAttribute(int index, Object value) { Object oldValue = null; - if (index >= attributes.length) { - Object[] newArray = new Object[index + 1]; - System.arraycopy(attributes, 0, newArray, 0, attributes.length); - attributes = newArray; - } else { - oldValue = attributes[index]; + synchronized (this) { + if (index >= attributes.length) { + Object[] newArray = new Object[index + 1]; + System.arraycopy(attributes, 0, newArray, 0, attributes.length); + attributes = newArray; + } else { + oldValue = attributes[index]; + } + attributes[index] = value; } - attributes[index] = value; return oldValue; } diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index c81d377b..6c93e8a8 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -105,6 +105,7 @@ public void addColumn(final Column column) { checkIndexStatus(column); lock(); + graphWriteLock(); try { final ColumnImpl columnImpl = (ColumnImpl) column; short id = idMap.getShort(columnImpl.getId()); @@ -139,6 +140,7 @@ public void addColumn(final Column column) { throw new IllegalArgumentException("The column " + column.getId() + " already exist"); } } finally { + graphWriteUnlock(); unlock(); } } diff --git a/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 599b485e..8aa47cd5 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -217,7 +217,6 @@ public GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { public void destroyGraphObserver(GraphObserverImpl graphObserver) { GraphViewImpl graphViewImpl = (GraphViewImpl) graphObserver.graph.getView(); - checkViewExist(graphViewImpl); graphViewImpl.destroyGraphObserver(graphObserver); } From d7b645b9e4724cfb7602c48d78f4175e61143abd Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 16 Apr 2022 09:31:09 +0200 Subject: [PATCH 083/271] Fix failing test --- src/main/java/org/gephi/graph/impl/GraphViewStore.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 8aa47cd5..39c49a87 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -217,6 +217,9 @@ public GraphObserverImpl createGraphObserver(Graph graph, boolean withDiff) { public void destroyGraphObserver(GraphObserverImpl graphObserver) { GraphViewImpl graphViewImpl = (GraphViewImpl) graphObserver.graph.getView(); + if (graphObserver.graphStore != this.graphStore) { + throw new RuntimeException("This observer doesn't belong to this store"); + } graphViewImpl.destroyGraphObserver(graphObserver); } From d356bbc0ac8dbac13d3d75158eb9139ff9d9c3ac Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 16 Apr 2022 09:36:23 +0200 Subject: [PATCH 084/271] Set version to 0.6.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 60bdd004..3a766a4c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.5-SNAPSHOT + 0.6.5 jar GraphStore From a19514e64ad4534683f691e5db2a34d009a4f87b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 16 Apr 2022 09:54:23 +0200 Subject: [PATCH 085/271] Set version to 0.6.6-SNAPSHOT [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3a766a4c..80410b89 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.5 + 0.6.6-SNAPSHOT jar GraphStore From 0cf16077f68c0a0a4d913b29a0115d8fc54e9491 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 16 Apr 2022 11:48:21 +0200 Subject: [PATCH 086/271] Revert using write lock for addColumn --- pom.xml | 2 +- src/main/java/org/gephi/graph/impl/ColumnStore.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 80410b89..6567a7a6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.6-SNAPSHOT + 0.6.6 jar GraphStore diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index 6c93e8a8..c81d377b 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -105,7 +105,6 @@ public void addColumn(final Column column) { checkIndexStatus(column); lock(); - graphWriteLock(); try { final ColumnImpl columnImpl = (ColumnImpl) column; short id = idMap.getShort(columnImpl.getId()); @@ -140,7 +139,6 @@ public void addColumn(final Column column) { throw new IllegalArgumentException("The column " + column.getId() + " already exist"); } } finally { - graphWriteUnlock(); unlock(); } } From b1686cae9b772c423c5cc3e9f860a06553e3bc97 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 16 Apr 2022 11:53:06 +0200 Subject: [PATCH 087/271] Set version to 0.6.7-SNAPSHOT [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6567a7a6..1be81e8b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.6 + 0.6.7-SNAPSHOT jar GraphStore From 2caf0435cf0a02838d5badf4c3829118eb33aac8 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 1 May 2022 16:22:34 +0200 Subject: [PATCH 088/271] Fix #149 --- .../org/gephi/graph/impl/ElementImpl.java | 8 +-- .../org/gephi/graph/impl/TimeIndexStore.java | 61 +++++-------------- 2 files changed, 18 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index 367999ff..7e7b528a 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -280,21 +280,21 @@ private void updateIndex(Column column, Object oldValue, Object newValue) { if (oldValue instanceof TimeMap) { timeIndexStore.remove((TimeMap) oldValue); } else if (oldValue != null) { - timeIndexStore.remove(oldValue, this); + timeIndexStore.remove(oldValue); } if (newValue instanceof TimeMap) { timeIndexStore.add((TimeMap) newValue); } else if (newValue != null) { - timeIndexStore.add(newValue, this); + timeIndexStore.add(newValue); } } else if (TimeSet.class.isAssignableFrom(columnImpl.getTypeClass())) { if (oldValue instanceof TimeSet) { - timeIndexStore.remove((TimeSet) oldValue); + timeIndexStore.remove((TimeSet) oldValue, this); } else if (oldValue != null) { timeIndexStore.remove(oldValue, this); } if (newValue instanceof TimeSet) { - timeIndexStore.add((TimeSet) newValue); + timeIndexStore.add((TimeSet) newValue, this); } else if (newValue != null) { timeIndexStore.add(newValue, this); } diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java index e4958068..61632f1b 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java @@ -65,7 +65,7 @@ protected TimeIndexStore(Class type, TableLockImpl lock, boolean indexed, Map protected abstract TimeIndexImpl createIndex(boolean main); - public Integer add(K k) { + protected Integer add(K k) { checkK(k); lock(); @@ -91,7 +91,7 @@ public Integer add(K k) { } } - public int add(K k, Element element) { + public void add(K k, Element element) { lock(); try { int timeIndex = add(k); @@ -111,8 +111,6 @@ public int add(K k, Element element) { } } - - return timeIndex; } finally { unlock(); } @@ -124,13 +122,13 @@ public void add(TimeMap timeMap) { } } - public void add(TimeSet timeSet) { + public void add(TimeSet timeSet, Element element) { for (K timeKey : timeSet.toArray()) { - add(timeKey); + add(timeKey, element); } } - public Integer remove(K k) { + protected Integer remove(K k) { lock(); try { checkK(k); @@ -148,11 +146,13 @@ public Integer remove(K k) { } } - public int remove(K k, Element element) { + public void remove(K k, Element element) { lock(); try { Integer timeIndex = remove(k); - checkTimeIndex(timeIndex); + if (timeIndex == null) { + return; + } if (mainIndex != null) { mainIndex.remove(timeIndex, element); @@ -172,7 +172,6 @@ public int remove(K k, Element element) { } } - return timeIndex; } finally { unlock(); } @@ -184,9 +183,9 @@ public void remove(M timeMap) { } } - public void remove(S timeSet) { + public void remove(S timeSet, Element element) { for (K timeKey : timeSet.toArray()) { - remove(timeKey); + remove(timeKey, element); } } @@ -207,7 +206,7 @@ public void index(Element element) { S timeSet = getTimeSet(element); if (timeSet != null) { - add(timeSet); + add(timeSet, element); } synchronized (element) { @@ -218,15 +217,6 @@ public void index(Element element) { } } } - - if (timeSet != null && mainIndex != null) { - K[] ts = timeSet.toArray(); - int tsLength = ts.length; - for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - mainIndex.add(timestampIndex, element); - } - } } finally { unlock(); } @@ -237,36 +227,13 @@ public void clear(Element element) { try { S timeSet = getTimeSet(element); - if (timeSet != null && mainIndex != null) { - K[] ts = timeSet.toArray(); - int tsLength = ts.length; - for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - mainIndex.remove(timestampIndex, element); - } - - if (!viewIndexes.isEmpty()) { - for (Entry entry : viewIndexes.entrySet()) { - GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); - DirectedSubgraph graph = graphView.getDirectedGraph(); - boolean node = element instanceof Node; - if (node ? graph.contains((Node) element) : graph.contains((Edge) element)) { - for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - entry.getValue().remove(timestampIndex, element); - } - } - } - } - } - if (timeSet != null) { - remove(timeSet); + remove(timeSet, element); } synchronized (element) { for (Object val : element.getAttributes()) { - if (val != null && val instanceof TimeMap) { + if (val instanceof TimeMap) { TimeMap dynamicValue = (TimeMap) val; remove((M) dynamicValue); } From 0553c7bbe52fc53615e2c3a291190fea2dde0baa Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 1 May 2022 22:37:33 +0200 Subject: [PATCH 089/271] Set version to 0.6.7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1be81e8b..c147115f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.7-SNAPSHOT + 0.6.7 jar GraphStore From 867156f490ec3c8e2515da5da2ea87f2658635bd Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 1 May 2022 22:43:37 +0200 Subject: [PATCH 090/271] Set version to 0.6.8-SNAPSHOT [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c147115f..0318eb60 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.7 + 0.6.8-SNAPSHOT jar GraphStore From 7bc64065c3eade1c5171108ad95595c76b2732c4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 10 Jun 2022 22:25:40 +0200 Subject: [PATCH 091/271] Tackle #150 --- .../org/gephi/graph/impl/ColumnStore.java | 75 ++++------------ .../gephi/graph/impl/DefaultColumnsImpl.java | 34 +++++-- .../java/org/gephi/graph/impl/EdgeImpl.java | 12 ++- .../org/gephi/graph/impl/ElementImpl.java | 10 +-- .../org/gephi/graph/impl/GraphModelImpl.java | 4 +- .../java/org/gephi/graph/impl/GraphStore.java | 3 + .../java/org/gephi/graph/impl/IndexImpl.java | 90 ++++++++----------- .../java/org/gephi/graph/impl/IndexStore.java | 81 +++++++++-------- .../java/org/gephi/graph/impl/NodeImpl.java | 8 ++ .../java/org/gephi/graph/impl/TableImpl.java | 2 +- 10 files changed, 149 insertions(+), 170 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index c81d377b..964510d0 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -147,7 +147,6 @@ public void removeColumn(final Column column) { checkNonNullColumnObject(column); lock(); - graphWriteLock(); try { final ColumnImpl columnImpl = (ColumnImpl) column; @@ -172,73 +171,47 @@ public void removeColumn(final Column column) { columnImpl.setStoreId(NULL_ID); updateConfiguration(column); } finally { - graphWriteUnlock(); unlock(); } } public void removeColumn(final String key) { checkNonNullObject(key); - lock(); - try { - removeColumn(getColumn(key)); - } finally { - unlock(); - } + removeColumn(getColumn(key)); } public int getColumnIndex(final String key) { checkNonNullObject(key); - lock(); - try { - short id = idMap.getShort(key.toLowerCase()); - if (id == NULL_SHORT) { - throw new IllegalArgumentException("The column doesnt exist"); - } - return shortToInt(id); - } finally { - unlock(); + short id = idMap.getShort(key.toLowerCase()); + if (id == NULL_SHORT) { + throw new IllegalArgumentException("The column doesnt exist"); } + return shortToInt(id); } - public Column getColumnByIndex(final int index) { - lock(); - try { - if (index < 0 || index >= columns.length) { - throw new IllegalArgumentException("The column doesnt exist"); - } - ColumnImpl a = columns[index]; - if (a == null) { - throw new IllegalArgumentException("The column doesnt exist"); - } - return a; - } finally { - unlock(); + public ColumnImpl getColumnByIndex(final int index) { + if (index < 0 || index >= columns.length) { + throw new IllegalArgumentException("The column doesnt exist"); + } + ColumnImpl a = columns[index]; + if (a == null) { + throw new IllegalArgumentException("The column doesnt exist"); } + return a; } public ColumnImpl getColumn(final String key) { checkNonNullObject(key); - lock(); - try { - short id = idMap.getShort(key.toLowerCase()); - if (id == NULL_SHORT) { - return null; - } - return columns[shortToInt(id)]; - } finally { - unlock(); + short id = idMap.getShort(key.toLowerCase()); + if (id == NULL_SHORT) { + return null; } + return columns[shortToInt(id)]; } public boolean hasColumn(String key) { checkNonNullObject(key); - lock(); - try { - return idMap.containsKey(key.toLowerCase()); - } finally { - unlock(); - } + return idMap.containsKey(key.toLowerCase()); } @Override @@ -363,18 +336,6 @@ void unlock() { } } - void graphWriteLock() { - if (graphStore != null) { - graphStore.autoWriteLock(); - } - } - - void graphWriteUnlock() { - if (graphStore != null) { - graphStore.autoWriteUnlock(); - } - } - void checkNonNullObject(final Object o) { if (o == null) { throw new NullPointerException(); diff --git a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java index 24d7da5a..2dfcafae 100644 --- a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java +++ b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java @@ -1,13 +1,20 @@ package org.gephi.graph.impl; import org.gephi.graph.api.Column; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Element; import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; public class DefaultColumnsImpl implements GraphModel.DefaultColumns { protected final GraphStore store; + // Default columns (initialised at store creation) + protected final TableDefaultColumns nodeDefaultColumns; + protected final TableDefaultColumns edgeDefaultColumns; + // Extra columns (temporary solution, until they are fully added as normal // columns) protected final ColumnImpl degreeColumn; @@ -17,6 +24,8 @@ public class DefaultColumnsImpl implements GraphModel.DefaultColumns { public DefaultColumnsImpl(GraphStore store) { this.store = store; + this.nodeDefaultColumns = new TableDefaultColumns<>(store.nodeTable); + this.edgeDefaultColumns = new TableDefaultColumns<>(store.edgeTable); degreeColumn = new ColumnImpl(store.nodeTable, GraphStoreConfiguration.NODE_DEGREE_COLUMN_ID, Integer.class, "Degree", null, Origin.PROPERTY, false, true); @@ -30,12 +39,12 @@ public DefaultColumnsImpl(GraphStore store) { @Override public Column nodeId() { - return store.nodeTable.getColumn(GraphStoreConfiguration.ELEMENT_ID_INDEX); + return nodeDefaultColumns.id; } @Override public Column edgeId() { - return store.edgeTable.getColumn(GraphStoreConfiguration.ELEMENT_ID_INDEX); + return edgeDefaultColumns.id; } public Column edgeWeight() { @@ -44,22 +53,22 @@ public Column edgeWeight() { @Override public Column nodeLabel() { - return store.nodeTable.getColumn(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); + return nodeDefaultColumns.label; } @Override public Column edgeLabel() { - return store.edgeTable.getColumn(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); + return edgeDefaultColumns.label; } @Override public Column nodeTimeSet() { - return store.nodeTable.getColumn(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); + return nodeDefaultColumns.timeset; } @Override public Column edgeTimeSet() { - return store.edgeTable.getColumn(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); + return edgeDefaultColumns.timeset; } @Override @@ -81,4 +90,17 @@ public Column outDegree() { public Column edgeType() { return typeColumn; } + + protected static class TableDefaultColumns { + + protected final ColumnImpl id; + protected final ColumnImpl label; + protected final ColumnImpl timeset; + + public TableDefaultColumns(TableImpl table) { + this.id = table.getColumn(GraphStoreConfiguration.ELEMENT_ID_INDEX); + this.label = table.getColumn(GraphStoreConfiguration.ELEMENT_LABEL_INDEX); + this.timeset = table.getColumn(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); + } + } } diff --git a/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/src/main/java/org/gephi/graph/impl/EdgeImpl.java index 94a85fd6..ab2dbf1b 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -96,13 +96,13 @@ public boolean hasDynamicWeight() { @Override public void setWeight(double weight, double timestamp) { checkWeightDynamicType(); - setAttribute(graphStore.graphModel.defaultColumns.edgeWeight(), weight, timestamp); + setAttribute(graphStore.defaultColumns.edgeWeight(), weight, timestamp); } @Override public void setWeight(double weight, Interval interval) { checkWeightDynamicType(); - setAttribute(graphStore.graphModel.defaultColumns.edgeWeight(), weight, interval); + setAttribute(graphStore.defaultColumns.edgeWeight(), weight, interval); } @Override @@ -231,6 +231,14 @@ ColumnStore getColumnStore() { return null; } + @Override + DefaultColumnsImpl.TableDefaultColumns getDefaultColumns() { + if (graphStore != null) { + return graphStore.defaultColumns.edgeDefaultColumns; + } + return null; + } + @Override TimeIndexStore getTimeIndexStore() { if (graphStore != null) { diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index 7e7b528a..8fda712d 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -72,6 +72,8 @@ public ElementImpl(Object id, GraphStore graphStore) { abstract boolean isValid(); + abstract DefaultColumnsImpl.TableDefaultColumns getDefaultColumns(); + @Override public Object getId() { return attributes.getId(); @@ -85,7 +87,7 @@ public String getLabel() { @Override public void setLabel(String label) { if (GraphStoreConfiguration.ENABLE_ELEMENT_LABEL) { - setAttribute(getColumnStore().getColumnByIndex(GraphStoreConfiguration.ELEMENT_LABEL_INDEX), label); + setAttribute(getDefaultColumns().label, label); } } @@ -326,8 +328,7 @@ private boolean addTime(Object timeObject) { boolean res = attributes.addTime(timeObject); if (res) { - Column column = getColumnStore().getColumnByIndex(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); - updateIndex(column, null, timeObject); + updateIndex(getDefaultColumns().timeset, null, timeObject); } return res; } @@ -350,8 +351,7 @@ private boolean removeTime(Object timeObject) { boolean res = attributes.removeTime(timeObject); if (res) { - Column column = getColumnStore().getColumnByIndex(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); - updateIndex(column, timeObject, null); + updateIndex(getDefaultColumns().timeset, timeObject, null); } return res; } diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 0caa9bd2..b12e211d 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -51,7 +51,6 @@ public class GraphModelImpl implements GraphModel { protected final Configuration configuration; protected final GraphStore store; protected final GraphBridgeImpl graphBridge; - protected final DefaultColumnsImpl defaultColumns; public GraphModelImpl() { this(new Configuration()); @@ -63,7 +62,6 @@ public GraphModelImpl(Configuration config) { configuration = config.copy(); store = new GraphStore(this); graphBridge = new GraphBridgeImpl(store); - defaultColumns = new DefaultColumnsImpl(store); } @Override @@ -141,7 +139,7 @@ public void setVisibleView(GraphView view) { @Override public DefaultColumns defaultColumns() { - return defaultColumns; + return store.defaultColumns; } @Override diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 4d48dfea..cb5755ee 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -74,6 +74,8 @@ public class GraphStore implements DirectedGraph, DirectedSubgraph { protected DateTimeZone timeZone; // Spatial context protected SpatialIndexImpl spatialIndex; + // Default columns + protected final DefaultColumnsImpl defaultColumns; public GraphStore() { this(null); @@ -136,6 +138,7 @@ public GraphStore(GraphModelImpl model) { } else { edgeTable.store.length++; } + defaultColumns = new DefaultColumnsImpl(this); } @Override diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index 216740fc..c0d81694 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -17,6 +17,7 @@ package org.gephi.graph.impl; import java.util.Collection; +import java.util.Collections; import java.util.Map; import java.util.Set; import org.gephi.graph.api.Column; @@ -27,7 +28,6 @@ public class IndexImpl implements Index { - protected final TableLockImpl lock; protected final ColumnStore columnStore; protected final Graph graph; protected ColumnIndexImpl[] columns; @@ -41,7 +41,6 @@ public IndexImpl(ColumnStore columnStore, Graph graph) { this.columnStore = columnStore; this.graph = graph; this.columns = new ColumnIndexImpl[0]; - this.lock = columnStore.lock; } @Override @@ -63,12 +62,11 @@ public ColumnIndex getColumnIndex(Column column) { public int count(Column column, Object value) { checkNonNullColumnObject(column); - lock(); - try { - return getIndex(column).count(value); - } finally { - unlock(); + ColumnIndexImpl index = getIndex(column); + if (index != null) { + return index.count(value); } + return 0; } public int count(String key, Object value) { @@ -81,12 +79,11 @@ public int count(String key, Object value) { public Iterable get(Column column, Object value) { checkNonNullColumnObject(column); - lock(); - try { - return getIndex(column).get(value); - } finally { - unlock(); + ColumnIndexImpl index = getIndex(column); + if (index != null) { + return index.get(value); } + return Collections.EMPTY_LIST; } public Iterable get(String key, Object value) { @@ -99,35 +96,33 @@ public Iterable get(String key, Object value) { public boolean isSortable(Column column) { checkNonNullColumnObject(column); - lock(); - try { - return getIndex(column).isSortable(); - } finally { - unlock(); + ColumnIndexImpl index = getIndex(column); + if (index != null) { + return index.isSortable(); } + return false; } @Override public Number getMinValue(Column column) { checkNonNullColumnObject(column); - lock(); - try { - return getIndex(column).getMinValue(); - } finally { - unlock(); + ColumnIndexImpl index = getIndex(column); + if (index != null) { + return index.getMinValue(); } + return null; } @Override public Number getMaxValue(Column column) { checkNonNullColumnObject(column); - lock(); - try { - return getIndex(column).getMaxValue(); - } finally { - unlock(); + + ColumnIndexImpl index = getIndex(column); + if (index != null) { + return index.getMaxValue(); } + return null; } public Iterable>> get(Column column) { @@ -140,34 +135,31 @@ public Iterable>> get(Column column) { public Collection values(Column column) { checkNonNullColumnObject(column); - lock(); - try { - return getIndex(column).values(); - } finally { - unlock(); + ColumnIndexImpl index = getIndex(column); + if (index != null) { + return index.values(); } + return Collections.EMPTY_LIST; } @Override public int countValues(Column column) { checkNonNullColumnObject(column); - lock(); - try { - return getIndex(column).countValues(); - } finally { - unlock(); + ColumnIndexImpl index = getIndex(column); + if (index != null) { + return index.countValues(); } + return 0; } @Override public int countElements(Column column) { checkNonNullColumnObject(column); - lock(); - try { - return getIndex(column).countElements(); - } finally { - unlock(); + ColumnIndexImpl index = getIndex(column); + if (index != null) { + return index.countElements(); } + return 0; } public Object put(String key, Object value, T element) { @@ -253,7 +245,7 @@ protected ColumnIndexImpl getIndex(Column col) { // TODO: Make this more robust if (col.isProperty()) { - DefaultColumnsImpl defaultColumns = columnStore.graphStore.graphModel.defaultColumns; + DefaultColumnsImpl defaultColumns = columnStore.graphStore.defaultColumns; if (col == defaultColumns.degreeColumn) { return new DegreeNoIndexImpl(graph, DegreeNoIndexImpl.DegreeType.DEGREE); } else if (col == defaultColumns.inDegreeColumn) { @@ -367,18 +359,6 @@ private void ensureColumnSize(int index) { } } - void lock() { - if (lock != null) { - lock.lock(); - } - } - - void unlock() { - if (lock != null) { - lock.unlock(); - } - } - void checkNonNullObject(final Object o) { if (o == null) { throw new NullPointerException(); diff --git a/src/main/java/org/gephi/graph/impl/IndexStore.java b/src/main/java/org/gephi/graph/impl/IndexStore.java index 5d05541a..7fd8ca16 100644 --- a/src/main/java/org/gephi/graph/impl/IndexStore.java +++ b/src/main/java/org/gephi/graph/impl/IndexStore.java @@ -42,6 +42,7 @@ public IndexStore(ColumnStore columnStore) { this.lock = columnStore.lock; } + // Table locked protected void addColumn(ColumnImpl col) { mainIndex.addColumn(col); for (IndexImpl index : viewIndexes.values()) { @@ -49,6 +50,7 @@ protected void addColumn(ColumnImpl col) { } } + // Table locked protected void removeColumn(ColumnImpl col) { mainIndex.removeColumn(col); for (IndexImpl index : viewIndexes.values()) { @@ -65,15 +67,12 @@ protected IndexImpl getIndex(Graph graph) { if (view.isMainView()) { return mainIndex; } - lock(); - try { + synchronized (viewIndexes) { IndexImpl viewIndex = viewIndexes.get(graph.getView()); if (viewIndex == null) { viewIndex = createViewIndex(graph); } return viewIndex; - } finally { - unlock(); } } @@ -81,14 +80,19 @@ protected IndexImpl createViewIndex(Graph graph) { if (graph.getView().isMainView()) { throw new IllegalArgumentException("Can't create a view index for the main view"); } - IndexImpl viewIndex = new IndexImpl<>(columnStore, graph); - ColumnImpl[] columns = columnStore.toArray(); - viewIndex.addAllColumns(columns); - viewIndexes.put(graph.getView(), viewIndex); + lock(); + try { + IndexImpl viewIndex = new IndexImpl<>(columnStore, graph); + ColumnImpl[] columns = columnStore.toArray(); + viewIndex.addAllColumns(columns); + viewIndexes.put(graph.getView(), viewIndex); - indexView(graph); + indexView(graph); - return viewIndex; + return viewIndex; + } finally { + unlock(); + } } protected void deleteViewIndex(Graph graph) { @@ -107,11 +111,10 @@ protected void deleteViewIndex(Graph graph) { } public Object set(Column column, Object oldValue, Object value, T element) { - lock(); - try { - value = mainIndex.set(column, oldValue, value, element); + value = mainIndex.set(column, oldValue, value, element); - if (!viewIndexes.isEmpty()) { + if (!viewIndexes.isEmpty()) { + synchronized (viewIndexes) { for (Entry> entry : viewIndexes.entrySet()) { GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); DirectedSubgraph graph = graphView.getDirectedGraph(); @@ -122,11 +125,9 @@ public Object set(Column column, Object oldValue, Object value, T element) { } } } - - return value; - } finally { - unlock(); } + + return value; } public void clear(T element) { @@ -141,13 +142,17 @@ public void clear(T element) { if (c != null && c.isIndexed()) { Object value = elementImpl.getAttribute(c); mainIndex.remove(c, value, element); - for (Entry> entry : viewIndexes.entrySet()) { - GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); - DirectedSubgraph graph = graphView.getDirectedGraph(); - boolean inView = element instanceof Node ? graph.contains((Node) element) - : graph.contains((Edge) element); - if (inView) { - entry.getValue().remove(c, value, element); + if (!viewIndexes.isEmpty()) { + synchronized (viewIndexes) { + for (Entry> entry : viewIndexes.entrySet()) { + GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); + DirectedSubgraph graph = graphView.getDirectedGraph(); + boolean inView = element instanceof Node ? graph.contains((Node) element) + : graph.contains((Edge) element); + if (inView) { + entry.getValue().remove(c, value, element); + } + } } } } @@ -194,14 +199,12 @@ public void indexView(Graph graph) { ElementImpl element = (ElementImpl) iterator.next(); final ColumnImpl[] cols = columnStore.columns; - synchronized (element) { - int length = columnStore.length; - for (int i = 0; i < length; i++) { - Column c = cols[i]; - if (c != null && c.isIndexed()) { - Object value = element.getAttribute(c); - viewIndex.put(c, value, element); - } + int length = columnStore.length; + for (int i = 0; i < length; i++) { + Column c = cols[i]; + if (c != null && c.isIndexed()) { + Object value = element.getAttribute(c); + viewIndex.put(c, value, element); } } } @@ -223,10 +226,8 @@ public void indexInView(T element, GraphView view) { for (int i = 0; i < length; i++) { Column c = cols[i]; if (c != null && c.isIndexed()) { - synchronized (elementImpl) { - Object value = elementImpl.getAttribute(c); - index.put(c, value, element); - } + Object value = elementImpl.getAttribute(c); + index.put(c, value, element); } } } @@ -246,10 +247,8 @@ public void clearInView(T element, GraphView view) { for (int i = 0; i < length; i++) { Column c = cols[i]; if (c != null && c.isIndexed()) { - synchronized (elementImpl) { - Object value = elementImpl.getAttribute(c); - index.remove(c, value, element); - } + Object value = elementImpl.getAttribute(c); + index.remove(c, value, element); } } } diff --git a/src/main/java/org/gephi/graph/impl/NodeImpl.java b/src/main/java/org/gephi/graph/impl/NodeImpl.java index b28a2c26..db350c78 100644 --- a/src/main/java/org/gephi/graph/impl/NodeImpl.java +++ b/src/main/java/org/gephi/graph/impl/NodeImpl.java @@ -76,6 +76,14 @@ ColumnStore getColumnStore() { return null; } + @Override + DefaultColumnsImpl.TableDefaultColumns getDefaultColumns() { + if (graphStore != null) { + return graphStore.defaultColumns.nodeDefaultColumns; + } + return null; + } + @Override public Table getTable() { if (graphStore != null) { diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index 7ace62a6..c8ac06e5 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -147,7 +147,7 @@ public List toList() { } @Override - public Column getColumn(int index) { + public ColumnImpl getColumn(int index) { return store.getColumnByIndex(index); } From c9b0b69893032170e7a6f33dc852fc2d2e132f67 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 14 Jun 2022 22:06:56 +0200 Subject: [PATCH 092/271] Fix issue with attribute reference update in index --- src/main/java/org/gephi/graph/impl/IndexStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/gephi/graph/impl/IndexStore.java b/src/main/java/org/gephi/graph/impl/IndexStore.java index 7fd8ca16..a39c1b6e 100644 --- a/src/main/java/org/gephi/graph/impl/IndexStore.java +++ b/src/main/java/org/gephi/graph/impl/IndexStore.java @@ -174,7 +174,7 @@ public void index(T element) { if (c != null && c.isIndexed()) { Object value = elementImpl.getAttribute(c); value = mainIndex.put(c, value, element); - elementImpl.setAttribute(c, value); + elementImpl.attributes.setAttribute(c, value); } } } finally { From 81842c53009cac8c8350a21ded525bf771bbac9a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 14 Jun 2022 22:19:15 +0200 Subject: [PATCH 093/271] Set version to 0.6.8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0318eb60..40686bfa 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.8-SNAPSHOT + 0.6.8 jar GraphStore From e752d7454760fb4d4cb0aa9c700dfe2bd43363ed Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 14 Jun 2022 22:24:17 +0200 Subject: [PATCH 094/271] Set version to 0.6.9-SNAPSHOT [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 40686bfa..0d636f1d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.8 + 0.6.9-SNAPSHOT jar GraphStore From 5b00f760ecb7f30a047d1fb565a3efc5e46c9dc5 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 17 Jun 2022 20:18:15 +0200 Subject: [PATCH 095/271] Fix issue with default column when changing configuration --- .../gephi/graph/impl/DefaultColumnsImpl.java | 9 ++++++-- .../org/gephi/graph/impl/GraphModelImpl.java | 1 + .../org/gephi/graph/impl/GraphModelTest.java | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java index 2dfcafae..e782383f 100644 --- a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java +++ b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java @@ -12,8 +12,8 @@ public class DefaultColumnsImpl implements GraphModel.DefaultColumns { protected final GraphStore store; // Default columns (initialised at store creation) - protected final TableDefaultColumns nodeDefaultColumns; - protected final TableDefaultColumns edgeDefaultColumns; + protected TableDefaultColumns nodeDefaultColumns; + protected TableDefaultColumns edgeDefaultColumns; // Extra columns (temporary solution, until they are fully added as normal // columns) @@ -37,6 +37,11 @@ public DefaultColumnsImpl(GraphStore store) { null, Origin.PROPERTY, false, true); } + public void resetConfiguration() { + this.nodeDefaultColumns = new TableDefaultColumns<>(store.nodeTable); + this.edgeDefaultColumns = new TableDefaultColumns<>(store.edgeTable); + } + @Override public Column nodeId() { return nodeDefaultColumns.id; diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index b12e211d..2e53c09a 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -533,6 +533,7 @@ public void setConfiguration(Configuration config) { } store.factory.resetConfiguration(); + store.defaultColumns.resetConfiguration(); } finally { store.autoWriteUnlock(); } diff --git a/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/src/test/java/org/gephi/graph/impl/GraphModelTest.java index 98d29285..75af99f7 100644 --- a/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -629,6 +629,28 @@ public void testSetConfigurationEdgeWeightColumnTrue() { .getIndex(), GraphStoreConfiguration.EDGE_WEIGHT_INDEX); } + @Test + public void testSetConfigurationDefaultColumns() { + Configuration config = new Configuration(); + GraphModelImpl graphModelImpl = new GraphModelImpl(config); + + Configuration newConfig = new Configuration(); + newConfig.setNodeIdType(Integer.class); + newConfig.setEdgeIdType(Integer.class); + newConfig.setTimeRepresentation(TimeRepresentation.TIMESTAMP); + newConfig.setEdgeWeightType(TimestampDoubleMap.class); + graphModelImpl.setConfiguration(newConfig); + + Assert.assertSame(graphModelImpl.defaultColumns().nodeId(), graphModelImpl.getNodeTable().getColumn("id")); + Assert.assertSame(graphModelImpl.defaultColumns().edgeId(), graphModelImpl.getEdgeTable().getColumn("id")); + Assert.assertSame(graphModelImpl.defaultColumns().nodeTimeSet(), graphModelImpl.getNodeTable() + .getColumn("timeset")); + Assert.assertSame(graphModelImpl.defaultColumns().edgeTimeSet(), graphModelImpl.getEdgeTable() + .getColumn("timeset")); + Assert.assertSame(graphModelImpl.defaultColumns().edgeWeight(), graphModelImpl.getEdgeTable() + .getColumn("weight")); + } + @Test public void testNodeAttributesAddAndRemoveColumns1() { GraphModelImpl graphModel = new GraphModelImpl(); From e9da87ba78cde33294a5f1eedf43588fd33952af Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 17 Jun 2022 20:18:39 +0200 Subject: [PATCH 096/271] Set version to 0.6.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0d636f1d..b4c51168 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.9-SNAPSHOT + 0.6.9 jar GraphStore From abf83abff301a105ca073c938e4e4bcbf958936f Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 17 Jun 2022 20:26:14 +0200 Subject: [PATCH 097/271] Set version to 0.6.10-SNAPSHOT [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b4c51168..7279cad4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.9 + 0.6.10-SNAPSHOT jar GraphStore From 252a0b7f26a63b33073e252fa4027d701f1c9831 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 17 Jul 2022 16:47:29 +0200 Subject: [PATCH 098/271] Fix #151 --- .../java/org/gephi/graph/impl/Serialization.java | 2 ++ .../org/gephi/graph/impl/SerializationTest.java | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index ed801c66..2ebc3761 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -243,6 +243,7 @@ public GraphModelImpl deserializeGraphModel(DataInput is) throws IOException, Cl Configuration config = (Configuration) deserialize(is); model = new GraphModelImpl(config); deserialize(is); + model.store.defaultColumns.resetConfiguration(); return model; } @@ -251,6 +252,7 @@ public GraphModelImpl deserializeGraphModelWithoutVersionPrefix(DataInput is, fl Configuration config = (Configuration) deserialize(is); model = new GraphModelImpl(config); deserialize(is); + model.store.defaultColumns.resetConfiguration(); return model; } diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 9607d0af..7b1b3a21 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -1209,6 +1209,21 @@ public void testSmallUndirectedGraphModel() throws Exception { Assert.assertTrue(read.deepEquals(gm)); } + @Test + public void testDefaultColumns() throws Exception { + GraphModelImpl gm = GraphGenerator.generateSmallUndirectedGraphStore().graphModel; + Serialization ser = new Serialization(gm); + + DataInputOutput dio = new DataInputOutput(); + ser.serializeGraphModel(dio, gm); + byte[] bytes = dio.toByteArray(); + + GraphModelImpl read = ser.deserializeGraphModel(dio.reset(bytes)); + Assert.assertSame(read.defaultColumns().nodeId(), read.getNodeTable().getColumn("id")); + Assert.assertSame(read.defaultColumns().nodeLabel(), read.getNodeTable().getColumn("label")); + Assert.assertSame(read.defaultColumns().edgeId(), read.getEdgeTable().getColumn("id")); + } + @Test public void testDeserializeWithoutVersion() throws Exception { GraphModelImpl gm = GraphGenerator.generateSmallGraphStore().graphModel; From 8c4f742b28b647b75bb016da33530b1dce91184d Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 17 Jul 2022 17:02:00 +0200 Subject: [PATCH 099/271] Set version to 0.6.10 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7279cad4..d4bc7e91 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.10-SNAPSHOT + 0.6.10 jar GraphStore From 3a665245625db456dc70b7d9f29afd3139c2e8c8 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 17 Jul 2022 17:07:13 +0200 Subject: [PATCH 100/271] Set version to 0.6.11-SNAPSHOT [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d4bc7e91..587b0027 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.10 + 0.6.11-SNAPSHOT jar GraphStore From 098fc80a24e1be7352049c4a86e98b0347d169ad Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 31 Aug 2022 13:28:08 +0200 Subject: [PATCH 101/271] Implement edge type edition #152 --- src/main/java/org/gephi/graph/api/Edge.java | 7 + .../java/org/gephi/graph/impl/EdgeImpl.java | 13 +- .../java/org/gephi/graph/impl/EdgeStore.java | 179 ++++++++++++------ .../org/gephi/graph/impl/EdgeTypeStore.java | 10 + .../java/org/gephi/graph/impl/GraphStore.java | 19 +- .../org/gephi/graph/impl/GraphViewImpl.java | 25 ++- .../org/gephi/graph/impl/GraphViewStore.java | 12 ++ .../org/gephi/graph/impl/BasicGraphStore.java | 7 +- .../org/gephi/graph/impl/EdgeStoreTest.java | 65 +++++++ .../org/gephi/graph/impl/GraphGenerator.java | 24 +++ .../org/gephi/graph/impl/GraphStoreTest.java | 18 ++ .../graph/impl/GraphViewDecoratorTest.java | 8 +- .../gephi/graph/impl/GraphViewImplTest.java | 49 +++++ 13 files changed, 353 insertions(+), 83 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/Edge.java b/src/main/java/org/gephi/graph/api/Edge.java index 307e8619..f269be0d 100644 --- a/src/main/java/org/gephi/graph/api/Edge.java +++ b/src/main/java/org/gephi/graph/api/Edge.java @@ -114,6 +114,13 @@ public interface Edge extends Element, EdgeProperties { */ public int getType(); + /** + * Sets the edge's type. + * + * @param type the type + */ + public void setType(int type); + /** * Returns the edge's type label. * diff --git a/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/src/main/java/org/gephi/graph/impl/EdgeImpl.java index ab2dbf1b..0f9b4667 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -37,7 +37,8 @@ public class EdgeImpl extends ElementImpl implements Edge { // Final Data protected final NodeImpl source; protected final NodeImpl target; - protected final int type; + // Edge type + protected int type; // Pointers protected int storeId = EdgeStore.NULL_ID; protected int nextOutEdge = EdgeStore.NULL_ID; @@ -144,6 +145,16 @@ public int getType() { return type; } + @Override + public void setType(int type) { + graphStore.autoWriteLock(); + try { + graphStore.edgeStore.setEdgeType(this, type); + } finally { + graphStore.autoWriteUnlock(); + } + } + @Override public Object getTypeLabel() { graphStore.autoReadLock(); diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 780e09ac..0120304f 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -559,6 +559,121 @@ private EdgeImpl getMutual(final EdgeImpl edge) { return get(edge.target, edge.source, edge.type, false); } + public boolean setEdgeType(final Edge e, int type) { + checkNonNullEdgeObject(e); + + if (e.getType() == type) { + return false; + } + + int oldType = e.getType(); + EdgeImpl edge = (EdgeImpl) e; + if (edge.storeId != EdgeStore.NULL_ID) { + ensureLongDictionaryCapacity(type); + Long2ObjectOpenCustomHashMap newDico = longDictionary[type]; + + long longId = getLongId(edge.source, edge.target, edge.isDirected()); + int[] newDicoValue = newDico.get(longId); + if (newDicoValue != null && !GraphStoreConfiguration.ENABLE_PARALLEL_EDGES) { + return false; + } + + edgeTypeStore.registerEdgeType(type); + boolean wasMutual = edge.isMutual(); + removeFromDico(edge, edge.storeId); + + removeOutEdge(edge); + removeInEdge(edge); + edge.type = type; + insertOutEdge(edge); + insertInEdge(edge); + + addToDico(newDico, newDicoValue, edge, longId); + + if (viewStore != null) { + viewStore.setEdgeType(edge, oldType, wasMutual); + } + + incrementVersion(); + } else { + edge.type = type; + } + + return true; + } + + private void removeFromDico(EdgeImpl edge, int id) { + int type = edge.type; + NodeImpl source = edge.source; + NodeImpl target = edge.target; + boolean directed = edge.isDirected(); + + Long2ObjectOpenCustomHashMap dico = longDictionary[type]; + long longId = getLongId(source, target, directed); + int[] dicoValue = dico.get(longId); + if (dicoValue.length == 1) { + dico.remove(longId); + } else { + int[] newDicoValue = new int[dicoValue.length - 1]; + int j = 0; + for (int i = 0; i < dicoValue.length; i++) { + int v = dicoValue[i]; + if (v != id) { + newDicoValue[j++] = v; + } + } + dico.put(longId, newDicoValue); + } + + if (directed && !edge.isSelfLoop()) { + int[] index = longDictionary[type].get(getLongId(edge.target, edge.source, true)); + if (index != null) { + for (int i = 0; i < index.length; i++) { + EdgeImpl mutual = get(index[i]); + if (mutual.isMutual()) { + edge.setMutual(false); + + mutual.setMutual(false); + source.mutualDegree--; + target.mutualDegree--; + mutualEdgesSize--; + mutualEdgesTypeSize[type]--; + break; + } + } + } + } + } + + private void addToDico(Long2ObjectOpenCustomHashMap dico, int[] dicoValue, EdgeImpl edge, long longId) { + if (dicoValue == null) { + dicoValue = new int[] { edge.storeId }; + } else { + dicoValue = Arrays.copyOf(dicoValue, dicoValue.length + 1); + dicoValue[dicoValue.length - 1] = edge.storeId; + } + dico.put(longId, dicoValue); + + if (edge.isDirected() && !edge.isSelfLoop()) { + int type = edge.type; + int[] index = longDictionary[type].get(getLongId(edge.target, edge.source, true)); + if (index != null) { + for (int i = 0; i < index.length; i++) { + EdgeImpl mutual = get(index[i]); + if (!mutual.isMutual()) { + mutual.setMutual(true); + edge.setMutual(true); + edge.source.mutualDegree++; + edge.target.mutualDegree++; + mutualEdgesSize++; + mutualEdgesTypeSize[type]++; + break; + } + } + } + } + } + @Override public boolean add(final Edge e) { checkNonNullEdgeObject(e); @@ -606,37 +721,13 @@ public boolean add(final Edge e) { source.outDegree++; target.inDegree++; - if (dicoValue == null) { - dicoValue = new int[] { edge.storeId }; - } else { - dicoValue = Arrays.copyOf(dicoValue, dicoValue.length + 1); - dicoValue[dicoValue.length - 1] = edge.storeId; - } - dico.put(longId, dicoValue); + addToDico(dico, dicoValue, edge, longId); if (viewStore != null) { viewStore.addEdge(edge); } edge.indexAttributes(); - if (directed && !edge.isSelfLoop()) { - int[] index = longDictionary[type].get(getLongId(edge.target, edge.source, true)); - if (index != null) { - for (int i = 0; i < index.length; i++) { - EdgeImpl mutual = get(index[i]); - if (!mutual.isMutual()) { - mutual.setMutual(true); - edge.setMutual(true); - source.mutualDegree++; - target.mutualDegree++; - mutualEdgesSize++; - mutualEdgesTypeSize[type]++; - break; - } - } - } - } - if (!directed) { undirectedSize++; } @@ -701,43 +792,7 @@ public boolean remove(final Object o) { } } - int type = edge.type; - - Long2ObjectOpenCustomHashMap dico = longDictionary[type]; - long longId = getLongId(source, target, directed); - int[] dicoValue = dico.get(longId); - if (dicoValue.length == 1) { - dico.remove(longId); - } else { - int[] newDicoValue = new int[dicoValue.length - 1]; - int j = 0; - for (int i = 0; i < dicoValue.length; i++) { - int v = dicoValue[i]; - if (v != id) { - newDicoValue[j++] = v; - } - } - dico.put(longId, newDicoValue); - } - - if (directed && !edge.isSelfLoop()) { - int[] index = longDictionary[type].get(getLongId(edge.target, edge.source, true)); - if (index != null) { - for (int i = 0; i < index.length; i++) { - EdgeImpl mutual = get(index[i]); - if (mutual.isMutual()) { - edge.setMutual(true); - - mutual.setMutual(false); - source.mutualDegree--; - target.mutualDegree--; - mutualEdgesSize--; - mutualEdgesTypeSize[type]--; - break; - } - } - } - } + removeFromDico(edge, id); if (!directed) { undirectedSize--; diff --git a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java index 8f325419..bd052c92 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java @@ -76,6 +76,16 @@ public Object getLabel(final int id) { return idMap.get(intToShort(id)); } + public void registerEdgeType(int type) { + if (!contains(type)) { + if (GraphStoreConfiguration.ENABLE_AUTO_TYPE_REGISTRATION) { + addType(String.valueOf(type), type); + } else { + throw new RuntimeException("The type doesn't exist"); + } + } + } + public int addType(final Object label) { checkType(label); diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index cb5755ee..09cb3c07 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -165,7 +165,9 @@ public boolean addAllNodes(final Collection nodes) { public boolean addEdge(final Edge edge) { autoWriteLock(); try { - registerEdgeType(edge); + if (edgeTypeStore != null) { + edgeTypeStore.registerEdgeType(edge.getType()); + } return edgeStore.add(edge); } finally { autoWriteUnlock(); @@ -177,7 +179,9 @@ public boolean addAllEdges(Collection edges) { autoWriteLock(); try { for (Edge edge : edges) { - registerEdgeType(edge); + if (edgeTypeStore != null) { + edgeTypeStore.registerEdgeType(edge.getType()); + } } return edgeStore.addAll(edges); } finally { @@ -185,17 +189,6 @@ public boolean addAllEdges(Collection edges) { } } - private void registerEdgeType(Edge edge) { - int type = edge.getType(); - if (edgeTypeStore != null && !edgeTypeStore.contains(type)) { - if (GraphStoreConfiguration.ENABLE_AUTO_TYPE_REGISTRATION) { - edgeTypeStore.addType(String.valueOf(type), type); - } else { - throw new RuntimeException("The type doesn't exist"); - } - } - } - @Override public NodeImpl getNode(final Object id) { autoReadLock(); diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index 8fc0f18a..083c7d66 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -660,6 +660,25 @@ protected void ensureEdgeVectorSize(EdgeImpl edge) { } } + protected void setEdgeType(EdgeImpl edgeImpl, int oldType, boolean wasMutual) { + ensureTypeCountArrayCapacity(edgeImpl.type); + typeCounts[oldType]--; + typeCounts[edgeImpl.type]++; + + if (!edgeImpl.isSelfLoop()) { + if (wasMutual && containsEdge(graphStore.edgeStore.get(edgeImpl.target, edgeImpl.source, oldType, false))) { + mutualEdgeTypeCounts[oldType]--; + mutualEdgesCount--; + } + + if (edgeImpl.isMutual() && containsEdge(graphStore.edgeStore + .get(edgeImpl.target, edgeImpl.source, edgeImpl.type, false))) { + mutualEdgeTypeCounts[edgeImpl.type]++; + mutualEdgesCount++; + } + } + } + private void addEdge(EdgeImpl edgeImpl) { incrementEdgeVersion(); @@ -671,7 +690,8 @@ private void addEdge(EdgeImpl edgeImpl) { typeCounts[type]++; - if (edgeImpl.isMutual() && edgeImpl.source.storeId < edgeImpl.target.storeId) { + if (edgeImpl.isMutual() && !edgeImpl.isSelfLoop() && containsEdge(graphStore.edgeStore + .get(edgeImpl.target, edgeImpl.source, edgeImpl.type, false))) { mutualEdgeTypeCounts[type]++; mutualEdgesCount++; } @@ -693,7 +713,8 @@ private void removeEdge(EdgeImpl edgeImpl) { edgeCount--; typeCounts[edgeImpl.type]--; - if (edgeImpl.isMutual() && edgeImpl.source.storeId < edgeImpl.target.storeId) { + if (edgeImpl.isMutual() && !edgeImpl.isSelfLoop() && containsEdge(graphStore.edgeStore + .get(edgeImpl.target, edgeImpl.source, edgeImpl.type, false))) { mutualEdgeTypeCounts[edgeImpl.type]--; mutualEdgesCount--; } diff --git a/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 39c49a87..6d0d7da5 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -258,6 +258,18 @@ protected void addEdge(EdgeImpl edge) { } } + protected void setEdgeType(EdgeImpl edge, int oldType, boolean wasMutual) { + if (views.length > 0) { + for (GraphViewImpl view : views) { + if (view != null) { + if ((view.nodeView && !view.edgeView) || (view.edgeView && view.containsEdge(edge))) { + view.setEdgeType(edge, oldType, wasMutual); + } + } + } + } + } + protected void removeEdge(EdgeImpl edge) { if (views.length > 0) { for (GraphViewImpl view : views) { diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index 2109b17b..84818c0a 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -916,7 +916,7 @@ public static class BasicEdge extends BasicElement implements Edge { protected final BasicNode source; protected final BasicNode target; - protected final int type; + protected int type; protected final boolean directed; protected double weight; @@ -959,6 +959,11 @@ public int getType() { return type; } + @Override + public void setType(int type) { + this.type = type; + } + @Override public Object getTypeLabel() { throw new UnsupportedOperationException("Not supported yet."); diff --git a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java index 3239b11b..c6950055 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java @@ -1298,6 +1298,17 @@ public void testMutualParallel() { Assert.assertEquals(n2.getUndirectedDegree(), 2); } + @Test + public void testRemoveMutualEdge() { + EdgeImpl[] edges = GraphGenerator.generateMutualEdges(1); + EdgeTypeStore edgeTypeStore = new EdgeTypeStore(); + EdgeStore edgeStore = new EdgeStore(edgeTypeStore, null, null, null, null); + edgeStore.addAll(Arrays.asList(edges)); + edgeStore.remove(edges[0]); + Assert.assertFalse(edges[0].isMutual()); + Assert.assertFalse(edges[1].isMutual()); + } + @Test public void testAddSelfLoop() { EdgeStore edgeStore = new EdgeStore(); @@ -1868,6 +1879,60 @@ public void testSelfLoopIterator() { Assert.assertEquals(count, selfLoops); } + @Test + public void testSetSameType() { + EdgeStore edgeStore = new EdgeStore(); + EdgeImpl edge = GraphGenerator.generateSingleEdge(4); + edgeStore.add(edge); + Assert.assertFalse(edgeStore.setEdgeType(edge, 4)); + } + + @Test + public void testSetTypeBeforeAdd() { + EdgeStore edgeStore = new EdgeStore(); + EdgeImpl edge = GraphGenerator.generateSingleEdge(4); + Assert.assertEquals(4, edge.getType()); + edgeStore.setEdgeType(edge, 1); + Assert.assertEquals(1, edge.getType()); + } + + @Test + public void testSetType() { + EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(), null, null, null, null); + EdgeImpl edge = GraphGenerator.generateSingleEdge(4); + edgeStore.add(edge); + edgeStore.setEdgeType(edge, 1); + Assert.assertEquals(1, edge.getType()); + Assert.assertSame(edge, edgeStore.get(edge.source, edge.target, 1, false)); + Assert.assertTrue(edgeStore.contains(edge.source, edge.target, 1)); + Assert.assertFalse(edgeStore.contains(edge.source, edge.target, 4)); + Assert.assertEquals(0, edgeStore.size(4)); + Assert.assertEquals(1, edgeStore.size(1)); + } + + @Test + public void testSetTypeWithMutualEdge() { + EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(), null, null, null, null); + EdgeImpl[] edges = GraphGenerator.generateMutualEdges(4); + edgeStore.addAll(Arrays.asList(edges)); + Assert.assertTrue(edges[0].isMutual()); + Assert.assertTrue(edges[1].isMutual()); + edgeStore.setEdgeType(edges[0], 1); + Assert.assertFalse(edges[0].isMutual()); + Assert.assertFalse(edges[1].isMutual()); + } + + // Can only run when GraphStoreConfiguration.ENABLE_PARALLEL_EDGES = false + @Test(enabled = false) + public void testReturnFalseWithoutParallelEdges() { + EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(), null, null, null, null); + EdgeImpl edge = GraphGenerator.generateSingleEdge(1); + EdgeImpl edge2 = new EdgeImpl('0', edge.graphStore, edge.source, edge.target, 2, 1.0, true); + Assert.assertTrue(edgeStore.add(edge)); + Assert.assertTrue(edgeStore.add(edge2)); + Assert.assertFalse(edgeStore.setEdgeType(edge, 2)); + } + /* * UTILITY METHODS */ diff --git a/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/src/test/java/org/gephi/graph/impl/GraphGenerator.java index 5d1336b4..eb6cd4e5 100644 --- a/src/test/java/org/gephi/graph/impl/GraphGenerator.java +++ b/src/test/java/org/gephi/graph/impl/GraphGenerator.java @@ -86,6 +86,16 @@ public static EdgeImpl generateSingleEdge(int type) { return generateEdgeList(1, type, true, false, false)[0]; } + public static EdgeImpl[] generateMutualEdges(int type) { + EdgeImpl e1 = generateSingleEdge(type); + EdgeImpl e2 = generateOppositeEdge(e1); + return new EdgeImpl[] { e1, e2 }; + } + + public static EdgeImpl generateOppositeEdge(EdgeImpl edge) { + return new EdgeImpl("-" + edge.getId().toString(), edge.target, edge.source, edge.type, 1.0, true); + } + public static EdgeImpl generateSelfLoop(int type, boolean directed) { NodeStore nodeStore = generateNodeStore(2); EdgeImpl edge = new EdgeImpl('0', nodeStore.get(0), nodeStore.get(0), type, 1.0, directed); @@ -452,6 +462,20 @@ public static GraphStore generateTinyGraphStoreWithSelfLoop() { return graphStore; } + public static GraphStore generateTinyGraphStoreWithMutualEdge() { + GraphModelImpl graphModel = new GraphModelImpl(new Configuration()); + GraphStore graphStore = graphModel.store; + NodeImpl n1 = new NodeImpl("1", graphStore); + NodeImpl n2 = new NodeImpl("2", graphStore); + EdgeImpl e1 = new EdgeImpl("0", graphStore, n1, n2, EdgeTypeStore.NULL_LABEL, 1.0, true); + EdgeImpl e2 = new EdgeImpl("1", graphStore, n2, n1, EdgeTypeStore.NULL_LABEL, 1.0, true); + graphStore.addNode(n1); + graphStore.addNode(n2); + graphStore.addEdge(e1); + graphStore.addEdge(e2); + return graphStore; + } + public static GraphStore generateSmallGraphStore() { int edgeCount = 100; GraphStore graphStore = new GraphModelImpl().store; diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index 20db901c..e791e79e 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -485,6 +485,24 @@ public void testAddEdge() { Assert.assertTrue(c); } + @Test + public void testAddEdgeWithSameType() { + GraphStore graphStore = new GraphStore(); + NodeImpl[] nodes = GraphGenerator.generateNodeList(2); + graphStore.addAllNodes(Arrays.asList(nodes)); + + EdgeImpl edge1 = new EdgeImpl("0", nodes[0], nodes[1], 0, 1.0, true); + EdgeImpl edge2 = new EdgeImpl("1", nodes[0], nodes[1], 0, 1.0, true); + boolean a = graphStore.addEdge(edge1); + boolean b = graphStore.addEdge(edge2); + + Assert.assertTrue(a); + Assert.assertTrue(b); + + Assert.assertTrue(graphStore.contains(edge1)); + Assert.assertTrue(graphStore.contains(edge2)); + } + @Test public void testAddEdgeTypeRegistration() { GraphStore graphStore = new GraphStore(); diff --git a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java index 658e12aa..453e0abd 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java @@ -101,8 +101,8 @@ public void testUndirectedAdd() { Assert.assertTrue(a); Assert.assertFalse(b); Assert.assertTrue(graph.contains(e)); - boolean mutualToIgnore = graphStore.edgeStore.isUndirectedToIgnore((EdgeImpl) e); - if (!mutualToIgnore) { + EdgeImpl mutualEdge = graphStore.edgeStore.getMutualEdge(e); + if (!(mutualEdge != null && !e.isSelfLoop() && graph.contains(mutualEdge))) { Assert.assertEquals(graph.getEdgeCount(), ++count); } } @@ -234,8 +234,8 @@ public void testUndirectedRemove() { Assert.assertTrue(a); Assert.assertFalse(b); Assert.assertFalse(graph.contains(e)); - boolean mutualToIgnore = graphStore.edgeStore.isUndirectedToIgnore((EdgeImpl) e); - if (!mutualToIgnore) { + EdgeImpl mutualEdge = graphStore.edgeStore.getMutualEdge(e); + if (!(mutualEdge != null && !e.isSelfLoop() && graph.contains(mutualEdge))) { Assert.assertEquals(graph.getEdgeCount(), --count); } } diff --git a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java index b1c523d2..875f6b23 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java @@ -458,4 +458,53 @@ public void testDefaultVisibleView() { Assert.assertNotNull(view); Assert.assertEquals(view, graphStore.mainGraphView); } + + @Test + public void testMutualCounts() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStoreWithMutualEdge(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(false, true); + view.fill(); + Assert.assertEquals(view.getUndirectedEdgeCount(), 1); + Assert.assertEquals(view.getEdgeCount(), 2); + view.removeEdge(graphStore.getEdge("1")); + Assert.assertEquals(view.getUndirectedEdgeCount(), 1); + } + + @Test + public void testEdgeViewSetEdgeType() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(false, true); + + EdgeImpl e0 = graphStore.getEdge("0"); + Assert.assertEquals(view.getEdgeCount(0), 0); + view.addEdge(e0); + Assert.assertEquals(view.getEdgeCount(0), 1); + e0.setType(1); + Assert.assertEquals(view.getEdgeCount(0), 0); + Assert.assertEquals(view.getEdgeCount(1), 1); + } + + @Test + public void testEdgeViewSetEdgeTypeMutualEdges() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStoreWithMutualEdge(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(true, false); + view.fill(); + + EdgeImpl e0 = graphStore.getEdge("0"); + e0.setType(1); + Assert.assertEquals(view.getEdgeCount(0), 1); + Assert.assertEquals(view.getEdgeCount(1), 1); + Assert.assertEquals(view.getUndirectedEdgeCount(0), 1); + Assert.assertEquals(view.getUndirectedEdgeCount(1), 1); + + EdgeImpl e1 = graphStore.getEdge("1"); + e1.setType(1); + Assert.assertEquals(view.getEdgeCount(0), 0); + Assert.assertEquals(view.getEdgeCount(1), 2); + Assert.assertEquals(view.getUndirectedEdgeCount(0), 0); + Assert.assertEquals(view.getUndirectedEdgeCount(1), 1); + } } From 9d7f7ccdd82e7fc512f1ee996b8f1647c9b3e747 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 31 Aug 2022 15:27:02 +0200 Subject: [PATCH 102/271] Set version to 0.6.11 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 587b0027..a4fabab1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.11-SNAPSHOT + 0.6.11 jar GraphStore From ea484fff2b99d8e516a23b14a838013da2a06d2d Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 31 Aug 2022 15:36:00 +0200 Subject: [PATCH 103/271] Set version to 0.6.12-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a4fabab1..3f17ef83 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.11 + 0.6.12-SNAPSHOT jar GraphStore From 42e80cfec92ed5a317177c0368b6be356eb95d89 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 14 Sep 2022 20:09:59 +0200 Subject: [PATCH 104/271] Fix #153 --- .../org/gephi/graph/impl/AttributesImpl.java | 19 +++++++++------ .../org/gephi/graph/impl/ElementImplTest.java | 23 +++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/AttributesImpl.java b/src/main/java/org/gephi/graph/impl/AttributesImpl.java index 0f3b8d6c..9d6fe09a 100644 --- a/src/main/java/org/gephi/graph/impl/AttributesImpl.java +++ b/src/main/java/org/gephi/graph/impl/AttributesImpl.java @@ -81,15 +81,20 @@ public Object getAttribute(int index) { protected Object getAttribute(Column column, Object timeObject, Estimator estimator) { int index = column.getIndex(); synchronized (this) { - TimeMap dynamicValue = null; + Object dynamicValue = null; if (index < attributes.length) { - dynamicValue = (TimeMap) attributes[index]; + dynamicValue = attributes[index]; } - if (dynamicValue != null && !dynamicValue.isEmpty()) { - if (estimator == null) { - return dynamicValue.get(timeObject, column.getDefaultValue()); - } else { - return dynamicValue.get((Interval) timeObject, estimator); + if (TimeSet.class.isAssignableFrom(column.getTypeClass())) { + return dynamicValue; + } else { + TimeMap timeMap = (TimeMap) dynamicValue; + if (timeMap != null && !timeMap.isEmpty()) { + if (estimator == null) { + return timeMap.get(timeObject, column.getDefaultValue()); + } else { + return timeMap.get((Interval) timeObject, estimator); + } } } } diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index 2b5b9428..689c9cc8 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -29,6 +29,7 @@ import org.gephi.graph.api.Origin; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; +import org.gephi.graph.api.types.TimeSet; import org.gephi.graph.api.types.TimestampBooleanMap; import org.gephi.graph.api.types.TimestampByteMap; import org.gephi.graph.api.types.TimestampCharMap; @@ -231,6 +232,22 @@ public void testSetAttributeTimeset() { Assert.assertEquals(node.getAttribute(column), ti); } + @Test + public void testSetAttributeTimestampSet() { + GraphStore store = new GraphStore(); + Column column = generateTimesetColumn(store); + + TimestampSet ti = new TimestampSet(); + ti.add(1.0); + ti.add(2.0); + + NodeImpl node = new NodeImpl("0", store); + node.setAttribute(column, ti); + + Assert.assertEquals(node.getAttribute(column), ti); + Assert.assertEquals(node.getAttribute(column, store.mainGraphView), ti); + } + @Test public void testReplaceAttribute() { GraphStore store = new GraphStore(); @@ -1227,6 +1244,12 @@ private Column generateIntervalColumn(GraphStore graphStore) { return graphStore.nodeTable.store.getColumn("age"); } + private Column generateTimesetColumn(GraphStore graphStore) { + graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "events", TimestampSet.class, + "Events", null, Origin.DATA, false, false)); + return graphStore.nodeTable.store.getColumn("events"); + } + // Properties size public int getElementPropertiesLength() { return 1 + (ENABLE_ELEMENT_LABEL ? 1 : 0) + (ENABLE_ELEMENT_TIME_SET ? 1 : 0); From 8fdd4618feb6e521efe67e61a27c9803c631e8d4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 14 Sep 2022 20:12:09 +0200 Subject: [PATCH 105/271] Set version to 0.6.12 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3f17ef83..40979ebf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.12-SNAPSHOT + 0.6.12 jar GraphStore From 693a572c73cea416e57b0c57b16ba95cf2080bb5 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 14 Sep 2022 20:22:10 +0200 Subject: [PATCH 106/271] Set version to 0.6.13-SNAPSHOT [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 40979ebf..776562fa 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.12 + 0.6.13-SNAPSHOT jar GraphStore From 4a2776273a1dd4d7c8169263413cfbd8473532b3 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 26 Sep 2022 20:15:18 +0200 Subject: [PATCH 107/271] Add test cases in AttributeUtils --- src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java index 0f3dcd70..2a1abbe6 100644 --- a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java +++ b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java @@ -74,7 +74,9 @@ public void testParseSimpleTypes() { Assert.assertEquals(AttributeUtils.parse("foo", String.class), "foo"); Assert.assertEquals(AttributeUtils.parse("0", Integer.class), 0); Assert.assertEquals(AttributeUtils.parse("0", Float.class), 0f); + Assert.assertEquals(AttributeUtils.parse("0.5", Float.class), 0.5f); Assert.assertEquals(AttributeUtils.parse("0", Double.class), 0.0); + Assert.assertEquals(AttributeUtils.parse("0.5", Double.class), 0.5); Assert.assertEquals(AttributeUtils.parse("0", Long.class), 0l); Assert.assertEquals(AttributeUtils.parse("0", Short.class), (short) 0); Assert.assertEquals(AttributeUtils.parse("0", Byte.class), (byte) 0); @@ -94,7 +96,9 @@ public void testParseSimpleTypes() { public void testParsePrimitiveTypes() { Assert.assertEquals(AttributeUtils.parse("0", int.class), 0); Assert.assertEquals(AttributeUtils.parse("0", float.class), 0f); + Assert.assertEquals(AttributeUtils.parse("0.5", float.class), 0.5f); Assert.assertEquals(AttributeUtils.parse("0", double.class), 0.0); + Assert.assertEquals(AttributeUtils.parse("0.5", double.class), 0.5); Assert.assertEquals(AttributeUtils.parse("0", long.class), 0l); Assert.assertEquals(AttributeUtils.parse("0", short.class), (short) 0); Assert.assertEquals(AttributeUtils.parse("0", byte.class), (byte) 0); From 80172d1189cc0b0c9f42ec43a7667a7d156015bd Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 6 Nov 2022 14:14:11 +0100 Subject: [PATCH 108/271] Fix #154 --- src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java | 5 +++-- src/main/java/org/gephi/graph/impl/ElementImpl.java | 7 ++++++- src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java | 8 +++++--- src/test/java/org/gephi/graph/impl/ElementImplTest.java | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java index 2a23e6e0..7731e8f6 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java @@ -26,6 +26,7 @@ import org.gephi.graph.api.Column; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; +import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.Node; @@ -138,7 +139,7 @@ public Number getMinValue() { double minN = Double.POSITIVE_INFINITY; while (elementIterator.hasNext()) { ElementImpl element = (ElementImpl) elementIterator.next(); - Number num = (Number) element.getAttribute(column, graph.getView()); + Number num = (Number) element.getAttribute(column, graph.getView(), Estimator.MIN); if (min == null || (num != null && num.doubleValue() < minN)) { if (num != null) { minN = num.doubleValue(); @@ -167,7 +168,7 @@ public Number getMaxValue() { while (elementIterator.hasNext()) { ElementImpl element = (ElementImpl) elementIterator.next(); - Number num = (Number) element.getAttribute(column, graph.getView()); + Number num = (Number) element.getAttribute(column, graph.getView(), Estimator.MAX); if (max == null || (num != null && num.doubleValue() > maxN)) { if (num != null) { maxN = num.doubleValue(); diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index 8fda712d..71a2f3d7 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -137,6 +137,11 @@ public Object getAttribute(String key, GraphView view) { @Override public Object getAttribute(Column column, GraphView view) { + Estimator estimator = column.getEstimator(); + return getAttribute(column, view, estimator); + } + + protected Object getAttribute(Column column, GraphView view, Estimator estimator) { checkColumn(column); if (!column.isDynamic()) { @@ -144,7 +149,7 @@ public Object getAttribute(Column column, GraphView view) { } else { Interval interval = view.getTimeInterval(); checkViewExist(view); - return attributes.getAttribute(column, interval, getEstimator(column)); + return attributes.getAttribute(column, interval, estimator); } } diff --git a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java index 34862c94..46d78721 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java @@ -17,6 +17,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import org.gephi.graph.api.Column; import org.gephi.graph.api.Edge; @@ -195,7 +196,7 @@ public void testDynamicAttribute() { Assert.assertTrue(priceIndex.isSortable()); Assert.assertEquals(priceIndex.getMinValue(), 100); - Assert.assertEquals(priceIndex.getMaxValue(), 100); + Assert.assertEquals(priceIndex.getMaxValue(), 150); Assert.assertEquals(priceIndex.values(), Collections.singletonList(100)); Assert.assertEquals(priceIndex.count(100), 1); Assert.assertEquals(priceIndex.countElements(), 1); @@ -211,8 +212,9 @@ public void testDynamicAttributeWithEstimator() { addNodeWithAttribute(graphStore, priceIndex.column, "1", t); priceIndex.column.setEstimator(Estimator.AVERAGE); - Assert.assertEquals(priceIndex.getMinValue(), 125.0); - Assert.assertEquals(priceIndex.getMaxValue(), 125.0); + Assert.assertEquals(priceIndex.getMinValue(), 100); + Assert.assertEquals(priceIndex.getMaxValue(), 150); + Assert.assertEquals(priceIndex.values(), Collections.singletonList(125.0)); } @Test diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index 689c9cc8..e4467c74 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -1246,7 +1246,7 @@ private Column generateIntervalColumn(GraphStore graphStore) { private Column generateTimesetColumn(GraphStore graphStore) { graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "events", TimestampSet.class, - "Events", null, Origin.DATA, false, false)); + "Events", null, Origin.DATA, false, false)); return graphStore.nodeTable.store.getColumn("events"); } From b85bfc5857302a6e5d8825a13925568b15d0ce83 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 6 Nov 2022 14:18:00 +0100 Subject: [PATCH 109/271] Fix #155 --- src/main/java/org/gephi/graph/api/AttributeUtils.java | 4 ++++ .../java/org/gephi/graph/impl/FormattingAndParsingUtils.java | 4 ---- src/test/java/org/gephi/graph/impl/ArraysParserTest.java | 2 +- src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java | 5 ++++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index 0b3d458d..fefd1bb0 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -351,6 +351,10 @@ public static Object parse(String str, Class typeClass, DateTimeZone timeZone) { return null; } + if (str.equalsIgnoreCase("null")) { + return null; + } + if (typeClass.isPrimitive()) { typeClass = getStandardizedType(typeClass);// For primitives we can // use auto-unboxing diff --git a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java index 21b7b151..bea1d2a5 100644 --- a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java +++ b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java @@ -176,10 +176,6 @@ protected static T convertValue(Class typeClass, String valString) { value = AttributeUtils.parse(valString, typeClass); } - if (value == null) { - throw new IllegalArgumentException("Invalid value for type: " + valString); - } - return (T) value; } diff --git a/src/test/java/org/gephi/graph/impl/ArraysParserTest.java b/src/test/java/org/gephi/graph/impl/ArraysParserTest.java index af57c936..38fe8167 100644 --- a/src/test/java/org/gephi/graph/impl/ArraysParserTest.java +++ b/src/test/java/org/gephi/graph/impl/ArraysParserTest.java @@ -144,7 +144,7 @@ public void testParseNull() { String[] a2 = ArraysParser.parseArray(String[].class, "[\"null\", null, 'null', value]"); Assert.assertEquals(new Boolean[] { false, null, false }, a1); - Assert.assertEquals(new String[] { "null", null, "null", "value" }, a2); + Assert.assertEquals(new String[] { null, null, null, "value" }, a2); } @Test(expectedExceptions = IllegalArgumentException.class) diff --git a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java index 2a1abbe6..63612b5c 100644 --- a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java +++ b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java @@ -141,7 +141,9 @@ public void testParseArrayTypes() { Assert.assertEquals(AttributeUtils.parse("[-1e6, 1, .001, 2e6]", double[].class).getClass(), double[].class); Assert.assertEquals(AttributeUtils - .parse("[' true ', 'null', null]", String[].class), new String[] { " true ", "null", null }); + .parse("[' true ', 'null', null]", String[].class), new String[] { " true ", null, null }); + Assert.assertEquals(AttributeUtils.parse("['null']", Integer[].class), new Integer[] { null }); + Assert.assertEquals(AttributeUtils.parse("['null']", Double[].class), new Double[] { null }); Assert.assertEquals(AttributeUtils .parse("['123456789123456789123456789123456789']", BigInteger[].class), new BigInteger[] { new BigInteger( "123456789123456789123456789123456789") }); @@ -297,6 +299,7 @@ public void testParseCharInvalid() { @Test public void testParseNull() { Assert.assertNull(AttributeUtils.parse(null, Integer.class)); + Assert.assertNull(AttributeUtils.parse("null", Integer.class)); } @Test From 450c419b7d5c7783d3e64d0a210902e3072cba9d Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 6 Nov 2022 14:57:22 +0100 Subject: [PATCH 110/271] Implement #156 --- .../java/org/gephi/graph/api/GraphModel.java | 21 +++++++++++++++++-- .../org/gephi/graph/impl/Serialization.java | 11 ++++++++++ .../gephi/graph/impl/SerializationTest.java | 15 +++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index 78e9ee8f..a748a566 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -101,7 +101,7 @@ public static class Factory { * * @return new instance */ - public static GraphModel newInstance() { + public static GraphModelImpl newInstance() { return new GraphModelImpl(); } @@ -111,7 +111,7 @@ public static GraphModel newInstance() { * @param config configuration * @return new instance */ - public static GraphModel newInstance(Configuration config) { + public static GraphModelImpl newInstance(Configuration config) { return new GraphModelImpl(config); } } @@ -137,6 +137,23 @@ public static GraphModel read(DataInput input) throws IOException { } } + /** + * Read the input into the given graph model. The provided graph + * model should be empty. + * + * @param input data input to read from + * @return the graphmodel passed as parameter + * @throws IOException if an io error occurs + */ + public static GraphModel read(DataInput input, GraphModel graphModel) throws IOException { + try { + org.gephi.graph.impl.Serialization s = new org.gephi.graph.impl.Serialization(); + return s.deserializeGraphModel(input, graphModel); + } catch (ClassNotFoundException e) { + throw new IOException(e); + } + } + /** * Read the input and return the read graph model without an * explicit version header in the input. To be used with old graphstore diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 2ebc3761..f70e4fe5 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -58,6 +58,7 @@ import java.util.Map; import java.util.Set; import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.TimeFormat; @@ -247,6 +248,16 @@ public GraphModelImpl deserializeGraphModel(DataInput is) throws IOException, Cl return model; } + public GraphModelImpl deserializeGraphModel(DataInput is, GraphModel graphModel) throws IOException, ClassNotFoundException { + model = (GraphModelImpl) graphModel; + readVersion = (Float) deserialize(is); + Configuration config = (Configuration) deserialize(is); + model.setConfiguration(config); + deserialize(is); + model.store.defaultColumns.resetConfiguration(); + return model; + } + public GraphModelImpl deserializeGraphModelWithoutVersionPrefix(DataInput is, float version) throws IOException, ClassNotFoundException { readVersion = version; Configuration config = (Configuration) deserialize(is); diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 7b1b3a21..9cd89e87 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -55,6 +55,7 @@ import java.util.Map; import java.util.Set; import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.TimeFormat; @@ -1224,6 +1225,20 @@ public void testDefaultColumns() throws Exception { Assert.assertSame(read.defaultColumns().edgeId(), read.getEdgeTable().getColumn("id")); } + @Test + public void testDeserializeWithGraphModel() throws Exception { + GraphModelImpl gm = GraphGenerator.generateSmallUndirectedGraphStore().graphModel; + Serialization ser = new Serialization(gm); + + DataInputOutput dio = new DataInputOutput(); + ser.serializeGraphModel(dio, gm); + byte[] bytes = dio.toByteArray(); + + GraphModelImpl read = GraphModel.Factory.newInstance(); + read = ser.deserializeGraphModel(dio.reset(bytes), read); + Assert.assertTrue(read.deepEquals(gm)); + } + @Test public void testDeserializeWithoutVersion() throws Exception { GraphModelImpl gm = GraphGenerator.generateSmallGraphStore().graphModel; From 48e648ffbadfb8c0563d294bf3e59e1c2f4adceb Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 6 Nov 2022 15:06:21 +0100 Subject: [PATCH 111/271] Set version to 0.6.13 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 776562fa..000802ea 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.13-SNAPSHOT + 0.6.13 jar GraphStore From 429e82a3b21967ffc08514bb4ce5ed8f1da5ddac Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 6 Nov 2022 15:11:20 +0100 Subject: [PATCH 112/271] Set version to 0.6.14 [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 000802ea..df41fa2a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.13 + 0.6.14-SNAPSHOT jar GraphStore From 3d5ff5757c20ed8d96785cb12e076a5615a40ab4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 23 Dec 2022 20:27:53 +0100 Subject: [PATCH 113/271] Upgrade dependencies --- pom.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index df41fa2a..b875cac8 100644 --- a/pom.xml +++ b/pom.xml @@ -59,13 +59,13 @@ org.testng testng - 6.14.3 + 7.7.0 test it.unimi.dsi fastutil - 8.3.0 + 8.5.11 colt @@ -75,7 +75,7 @@ joda-time joda-time - 2.10.3 + 2.12.2 @@ -85,22 +85,22 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.10.1 org.apache.maven.plugins maven-surefire-plugin - 2.22.2 + 3.0.0-M7 org.apache.maven.plugins maven-source-plugin - 3.1.0 + 3.2.1 org.apache.maven.plugins maven-javadoc-plugin - 3.1.1 + 3.4.1 org.apache.maven.plugins @@ -110,12 +110,12 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.13 org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.8 org.eluder.coveralls @@ -143,7 +143,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.2.0 + 3.3.0 From c4400983017c764a2944a751be29fb9133dca3e8 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 23 Dec 2022 21:03:40 +0100 Subject: [PATCH 114/271] Implement #157 --- src/main/java/org/gephi/graph/api/Graph.java | 18 ++++++ .../java/org/gephi/graph/api/Subgraph.java | 22 +++++++ .../java/org/gephi/graph/impl/EdgeStore.java | 3 +- .../java/org/gephi/graph/impl/GraphStore.java | 21 +++++++ .../gephi/graph/impl/GraphViewDecorator.java | 20 +++++++ .../org/gephi/graph/impl/GraphViewImpl.java | 58 +++++++++++++++++++ .../java/org/gephi/graph/impl/NodeStore.java | 3 +- .../gephi/graph/impl/UndirectedDecorator.java | 10 ++++ .../org/gephi/graph/impl/BasicGraphStore.java | 22 +++++++ .../org/gephi/graph/impl/GraphGenerator.java | 10 +++- .../org/gephi/graph/impl/GraphStoreTest.java | 34 +++++++++++ .../graph/impl/GraphViewDecoratorTest.java | 49 ++++++++++++++++ 12 files changed, 267 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/Graph.java b/src/main/java/org/gephi/graph/api/Graph.java index 5d64337c..ad56cc8d 100644 --- a/src/main/java/org/gephi/graph/api/Graph.java +++ b/src/main/java/org/gephi/graph/api/Graph.java @@ -89,6 +89,24 @@ public interface Graph { */ public boolean removeAllNodes(Collection nodes); + /** + * Retains only nodes in this graph that are contained in the specified + * collection. + * + * @param nodes the node collection + * @return true if at least one node has been removed, false otherwise + */ + public boolean retainNodes(Collection nodes); + + /** + * Retains only edges in this graph that are contained in the specified + * collection. + * + * @param edges the edge collection + * @return true if at least one edge has been removed, false otherwise + */ + public boolean retainEdges(Collection edges); + /** * Returns true if node is contained in this graph. * diff --git a/src/main/java/org/gephi/graph/api/Subgraph.java b/src/main/java/org/gephi/graph/api/Subgraph.java index 00e88a8b..8d8c4cd6 100644 --- a/src/main/java/org/gephi/graph/api/Subgraph.java +++ b/src/main/java/org/gephi/graph/api/Subgraph.java @@ -110,6 +110,17 @@ public interface Subgraph extends Graph { @Override public boolean removeAllNodes(Collection nodes); + /** + * Retains only nodes in this subgraph that are contained in the specified + * collection. + *

+ * The nodes should be part of the root graph. + * + * @param nodes the node collection + * @return true if at least one node has been removed, false otherwise + */ + public boolean retainNodes(Collection nodes); + /** * Removes an edge from this subgraph. *

@@ -132,6 +143,17 @@ public interface Subgraph extends Graph { @Override public boolean removeAllEdges(Collection edges); + /** + * Retains only edges in this subgraph that are contained in the specified + * collection. + *

+ * The edges should be part of the root graph. + * + * @param edges the edge collection + * @return true if at least one edge has been removed, false otherwise + */ + public boolean retainEdges(Collection edges); + /** * Fills the subgraph so all elements in the graph are in the subgraph. */ diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 0120304f..3707b6fc 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -1021,8 +1021,9 @@ public boolean retainAll(Collection c) { } } return changed; - } else { + } else if (size > 0) { clear(); + return true; } return false; } diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 09cb3c07..77386112 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -16,6 +16,7 @@ package org.gephi.graph.impl; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -297,6 +298,16 @@ public boolean removeAllNodes(Collection nodes) { } } + @Override + public boolean retainNodes(Collection nodes) { + autoWriteLock(); + try { + return nodeStore.retainAll(nodes); + } finally { + autoWriteUnlock(); + } + } + @Override public boolean removeAllEdges(Collection edges) { autoWriteLock(); @@ -307,6 +318,16 @@ public boolean removeAllEdges(Collection edges) { } } + @Override + public boolean retainEdges(Collection edges) { + autoWriteLock(); + try { + return edgeStore.retainAll(edges); + } finally { + autoWriteUnlock(); + } + } + public NodeStore getNodeStore() { return nodeStore; } diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index d3bfdc94..f949c7f0 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -264,6 +264,26 @@ public boolean removeAllNodes(Collection nodes) { } } + @Override + public boolean retainNodes(Collection nodes) { + graphStore.autoWriteLock(); + try { + return view.retainNodes(nodes); + } finally { + graphStore.autoWriteUnlock(); + } + } + + @Override + public boolean retainEdges(Collection edges) { + graphStore.autoWriteLock(); + try { + return view.retainEdges(edges); + } finally { + graphStore.autoWriteUnlock(); + } + } + @Override public boolean contains(Node node) { checkValidNodeObject(node); diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index 083c7d66..42b8f592 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -17,6 +17,9 @@ import cern.colt.bitvector.BitVector; import cern.colt.bitvector.QuickBitVector; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -264,6 +267,61 @@ public boolean removeNodeAll(final Collection nodes) { return false; } + public boolean retainNodes(final Collection c) { + if (nodeView) { + if (!c.isEmpty()) { + IntOpenHashSet set = new IntOpenHashSet(c.size()); + for (Node o : c) { + checkValidNodeObject(o); + set.add(o.getStoreId()); + } + + boolean changed = false; + int nodeSize = nodeBitVector.size(); + for (int i = 0; i < nodeSize; i++) { + boolean t = nodeBitVector.get(i); + if (t && !set.contains(i)) { + if (removeNode(getNode(i))) { + changed = true; + } + } + } + return changed; + } else if (nodeCount != 0) { + clear(); + return true; + } + } + return false; + } + + public boolean retainEdges(final Collection c) { + if (edgeView) { + if (!c.isEmpty()) { + IntOpenHashSet set = new IntOpenHashSet(c.size()); + for (Edge o : c) { + checkValidEdgeObject(o); + set.add(o.getStoreId()); + } + + boolean changed = false; + int edgeSize = edgeBitVector.size(); + for (int i = 0; i < edgeSize; i++) { + boolean t = edgeBitVector.get(i); + if (t && !set.contains(i)) { + removeEdge(getEdge(i)); + changed = true; + } + } + return changed; + } else if (edgeCount != 0) { + clearEdges(); + return true; + } + } + return false; + } + public boolean removeEdge(final Edge edge) { checkEdgeView(); diff --git a/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java index 841eb1a7..9c2c8add 100644 --- a/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -443,8 +443,9 @@ public boolean retainAll(final Collection c) { } } return changed; - } else { + } else if (size > 0) { clear(); + return true; } return false; } diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 189d5885..13ea499a 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -87,6 +87,16 @@ public boolean removeAllNodes(Collection nodes) { return store.removeAllNodes(nodes); } + @Override + public boolean retainNodes(Collection nodes) { + return store.retainNodes(nodes); + } + + @Override + public boolean retainEdges(Collection edges) { + return store.retainEdges(edges); + } + @Override public boolean contains(Node node) { return store.contains(node); diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index 84818c0a..ddd0fc68 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -172,6 +172,28 @@ public boolean removeAllNodes(Collection nodes) { return true; } + @Override + public boolean retainNodes(Collection nodes) { + Set nodeSet = new HashSet<>(nodes); + for (Node n : nodes) { + if (!nodeSet.contains(n)) { + removeNode(n); + } + } + return true; + } + + @Override + public boolean retainEdges(Collection edges) { + Set edgeSet = new HashSet<>(edges); + for (Edge e : edges) { + if (!edgeSet.contains(e)) { + removeEdge(e); + } + } + return true; + } + @Override public boolean contains(Node node) { return nodeStore.contains(node); diff --git a/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/src/test/java/org/gephi/graph/impl/GraphGenerator.java index eb6cd4e5..e81601c1 100644 --- a/src/test/java/org/gephi/graph/impl/GraphGenerator.java +++ b/src/test/java/org/gephi/graph/impl/GraphGenerator.java @@ -477,12 +477,20 @@ public static GraphStore generateTinyGraphStoreWithMutualEdge() { } public static GraphStore generateSmallGraphStore() { + return generateSmallGraphStore(true); + } + + public static GraphStore generateSmallGraphStoreWithoutSelfLoop() { + return generateSmallGraphStore(false); + } + + private static GraphStore generateSmallGraphStore(boolean allowSelfLoops) { int edgeCount = 100; GraphStore graphStore = new GraphModelImpl().store; NodeImpl[] nodes = generateNodeList(Math .max((int) Math.ceil(Math.sqrt(edgeCount * 2)), (int) (edgeCount / 10.0)), graphStore); graphStore.addAllNodes(Arrays.asList(nodes)); - EdgeImpl[] edges = generateEdgeList(graphStore.nodeStore, edgeCount, 0, true, true, false); + EdgeImpl[] edges = generateEdgeList(graphStore.nodeStore, edgeCount, 0, true, allowSelfLoops, false); graphStore.addAllEdges(Arrays.asList(edges)); return graphStore; } diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index e791e79e..f732630c 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -772,6 +772,40 @@ public void testRemoveAllEdges() { } } + @Test + public void testRetainNodes() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + Node[] nodes = graphStore.getNodes().toArray(); + Assert.assertFalse(graphStore.retainNodes(Arrays.asList(nodes))); + Assert.assertEquals(graphStore.getNodeCount(), nodes.length); + + Assert.assertTrue(graphStore.retainNodes(Collections.EMPTY_LIST)); + Assert.assertEquals(graphStore.getNodeCount(), 0); + + graphStore = GraphGenerator.generateSmallGraphStore(); + nodes = graphStore.getNodes().toArray(); + graphStore.retainNodes(Collections.singletonList(nodes[0])); + Assert.assertEquals(graphStore.getNodeCount(), 1); + Assert.assertTrue(graphStore.contains(nodes[0])); + } + + @Test + public void testRetainEdges() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + Edge[] edges = graphStore.getEdges().toArray(); + Assert.assertFalse(graphStore.retainEdges(Arrays.asList(edges))); + Assert.assertEquals(graphStore.getEdgeCount(), edges.length); + + Assert.assertTrue(graphStore.retainEdges(Collections.EMPTY_LIST)); + Assert.assertEquals(graphStore.getEdgeCount(), 0); + + graphStore = GraphGenerator.generateSmallGraphStore(); + edges = graphStore.getEdges().toArray(); + graphStore.retainEdges(Collections.singletonList(edges[0])); + Assert.assertEquals(graphStore.getEdgeCount(), 1); + Assert.assertTrue(graphStore.contains(edges[0])); + } + @Test public void testGetOpposite() { GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); diff --git a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java index 453e0abd..09c7674a 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java @@ -17,6 +17,8 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; +import java.util.Arrays; +import java.util.Collections; import java.util.Random; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; @@ -356,6 +358,53 @@ public void testUndirectedRemoveNodesFirst() { Assert.assertEquals(graph.getEdgeCount(), 0); } + @Test + public void testDirectedRetainNodes() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStoreWithoutSelfLoop(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + DirectedSubgraph graph = store.getDirectedGraph(view); + view.fill(); + + Assert.assertFalse(graph.retainNodes(graphStore.getNodes().toCollection())); + Assert.assertEquals(graph.getNodeCount(), graphStore.getNodeCount()); + + Assert.assertTrue(graph.retainNodes(Collections.EMPTY_LIST)); + Assert.assertEquals(graph.getNodeCount(), 0); + + view.fill(); + Edge edge = graphStore.getEdges().toArray()[0]; + Assert.assertTrue(graph.retainNodes(Arrays.asList(edge.getSource(), edge.getTarget()))); + Assert.assertEquals(graph.getNodeCount(), 2); + Assert.assertTrue(graph.contains(edge.getSource())); + Assert.assertTrue(graph.contains(edge.getTarget())); + Assert.assertTrue(graph.contains(edge)); + } + + @Test + public void testDirectedRetainEdges() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStoreWithoutSelfLoop(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + DirectedSubgraph graph = store.getDirectedGraph(view); + view.fill(); + + Assert.assertFalse(graph.retainEdges(graphStore.getEdges().toCollection())); + Assert.assertEquals(graph.getEdgeCount(), graphStore.getEdgeCount()); + + Assert.assertTrue(graph.retainEdges(Collections.EMPTY_LIST)); + Assert.assertEquals(graph.getEdgeCount(), 0); + + view.fill(); + Edge edge = graphStore.getEdges().toArray()[0]; + Assert.assertTrue(graph.retainEdges(Collections.singletonList(edge))); + Assert.assertEquals(graph.getNodeCount(), graphStore.getNodeCount()); + Assert.assertEquals(graph.getEdgeCount(), 1); + Assert.assertTrue(graph.contains(edge)); + } + @Test public void testDirectedClearEdges() { GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); From 51306b78536f7219216feb11a26c34d630d95554 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 23 Dec 2022 21:39:32 +0100 Subject: [PATCH 115/271] Fix javadoc build --- pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pom.xml b/pom.xml index b875cac8..cd9e97f2 100644 --- a/pom.xml +++ b/pom.xml @@ -359,6 +359,8 @@ true true true + none + 11 From 5b81598690ec315d3cb2afd46d2616f054334df5 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 23 Dec 2022 21:47:42 +0100 Subject: [PATCH 116/271] Set version to 0.6.14 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd9e97f2..ecc7c4b8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.14-SNAPSHOT + 0.6.14 jar GraphStore From f15d6913720e6e9be6a89f0c75d2993afc397410 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 23 Dec 2022 21:54:17 +0100 Subject: [PATCH 117/271] Set version to 0.6.15-SNAPSHOT [ci skip] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ecc7c4b8..82c4d467 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.14 + 0.6.15-SNAPSHOT jar GraphStore From e071819feb7e4f580808f1402befe1b8d327150a Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Thu, 5 Jan 2023 15:37:49 +0100 Subject: [PATCH 118/271] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e83e155..cc52e7a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: [ master ] + branches: [ master, viz-engine ] jobs: build: From d490f6c1779a33addc1c8502082d8031c237cf3f Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 1 Mar 2023 21:31:41 +0100 Subject: [PATCH 119/271] Clean up all benchmark code --- README.md | 4 - pom.xml | 35 -- .../graph/benchmark/ControlBenchmarkTest.java | 54 -- .../benchmark/EdgeStoreBenchmarkTest.java | 93 --- .../benchmark/NodeStoreBenchmarkTest.java | 50 -- .../benchmarks/DataStructureBenchmark.java | 569 ------------------ .../benchmarks/EdgeStoreBenchmark.java | 110 ---- .../graph/benchmark/benchmarks/Generator.java | 61 -- .../benchmark/benchmarks/KleinbergGraph.java | 188 ------ .../benchmarks/LockingBenchmark.java | 270 --------- .../benchmarks/NodeStoreBenchmark.java | 84 --- .../benchmark/benchmarks/RandomGraph.java | 85 --- .../graph/benchmark/nanobench/NanoBench.java | 369 ------------ .../graph/benchmark/util/ReporterHandler.java | 50 -- 14 files changed, 2022 deletions(-) delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java delete mode 100644 src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java diff --git a/README.md b/README.md index 122d421c..351412d8 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,6 @@ GraphStore uses Maven for building. > mvn jacoco:report -## How to run the benchmark code - - > mvn integration-test - ## Contribute The source code is available under the Apache 2.0 license. Contributions are welcome. diff --git a/pom.xml b/pom.xml index 82c4d467..07d5505e 100644 --- a/pom.xml +++ b/pom.xml @@ -159,26 +159,6 @@ - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-benchmark-test-source - generate-test-sources - - add-test-source - - - - src/benchmark/java - - - - - - org.apache.maven.plugins @@ -198,24 +178,10 @@ test false - **/benchmark/** methods 4 - - - integration-test - - test - - integration-test - - false - **/benchmark/** - -Xmx2g - - @@ -282,7 +248,6 @@ ${project.build.sourceDirectory} ${project.build.testSourceDirectory} - ${project.basedir}/src/benchmark/java diff --git a/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java b/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java deleted file mode 100644 index f8f998d4..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/ControlBenchmarkTest.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import java.util.logging.Level; -import java.util.logging.Logger; -import org.gephi.graph.benchmark.nanobench.NanoBench; -import org.gephi.graph.benchmark.util.ReporterHandler; -import org.testng.Reporter; -import org.testng.annotations.BeforeSuite; -import org.testng.annotations.Test; - -public class ControlBenchmarkTest { - - @BeforeSuite - public void setUp() { - Logger logger = NanoBench.getLogger(); - logger.setUseParentHandlers(false); - logger.setLevel(Level.INFO); - logger.addHandler(new ReporterHandler()); - Reporter.setEscapeHtml(false); - } - - @Test - public void testControl() { - Runnable runnable = new Runnable() { - final int[] array = new int[10000000]; - int m = 0; - - @Override - public void run() { - int dummy = 0; - for (int doNotIgnoreMe : array) { - dummy += doNotIgnoreMe; - } - m += dummy; - } - }; - NanoBench.create().measure("control", runnable); - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java b/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java deleted file mode 100644 index 2b895204..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/EdgeStoreBenchmarkTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import org.gephi.graph.benchmark.benchmarks.EdgeStoreBenchmark; -import org.gephi.graph.benchmark.nanobench.NanoBench; -import org.testng.annotations.Test; - -public class EdgeStoreBenchmarkTest { - - @Test - public void testPushStore() { - int[] n = { 100, 1000, 5000 }; - double[] p = { 0.01, 0.1, 0.3 }; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2) - .measure("push edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() - .pushEdgeStore(nodes, prob)); - } - } - } - - @Test - public void testIterateStore() { - int[] n = { 100, 1000, 5000 }; - double[] p = { 0.01, 0.1, 0.3 }; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2) - .measure("iterate edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() - .iterateEdgeStore(nodes, prob)); - } - } - } - - @Test - public void testIterateOutNeighbors() { - int[] n = { 100, 1000, 5000 }; - double[] p = { 0.01, 0.1, 0.3 }; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2) - .measure("iterate neighbors list out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() - .iterateEdgeStoreNeighborsOut(nodes, prob)); - } - } - } - - @Test - public void testIterateInOutNeighbors() { - int[] n = { 100, 1000, 5000 }; - double[] p = { 0.01, 0.1, 0.3 }; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2) - .measure("iterate neighbors list in&out nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() - .iterateEdgeStoreNeighborsInOut(nodes, prob)); - } - } - } - - @Test - public void testResetEdgeStore() { - int[] n = { 100, 1000, 5000 }; - double[] p = { 0.01, 0.1, 0.3 }; - for (int nodes : n) { - for (double prob : p) { - int edges = (int) (nodes * (nodes - 1) * prob); - NanoBench.create().measurements(2) - .measure("reset edge store nodes=" + nodes + " edges=" + edges, new EdgeStoreBenchmark() - .resetEdgeStore(nodes, prob)); - } - } - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java b/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java deleted file mode 100644 index 902807ad..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/NodeStoreBenchmarkTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark; - -import org.gephi.graph.benchmark.benchmarks.NodeStoreBenchmark; -import org.gephi.graph.benchmark.nanobench.NanoBench; -import org.testng.annotations.Test; - -public class NodeStoreBenchmarkTest { - - @Test - public void testPushStore() { - int[] n = { 100, 1000, 10000, 100000 }; - for (int nodes : n) { - NanoBench.create().measurements(10) - .measure("push node store " + nodes, new NodeStoreBenchmark().pushStore(nodes)); - } - } - - @Test - public void testIterateStore() { - int[] n = { 100, 1000, 10000, 100000 }; - for (int nodes : n) { - NanoBench.create().cpuOnly().measurements(10) - .measure("iterate node store " + nodes, new NodeStoreBenchmark().iterateStore(nodes)); - } - } - - @Test - public void testResetNodeStore() { - int[] n = { 100, 1000, 10000, 100000 }; - for (int nodes : n) { - NanoBench.create().measurements(10) - .measure("reset node store " + nodes, new NodeStoreBenchmark().resetNodeStore(nodes)); - } - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java deleted file mode 100644 index 8f32672a..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/DataStructureBenchmark.java +++ /dev/null @@ -1,569 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark.benchmarks; - -import cern.colt.bitvector.BitVector; -import cern.colt.map.OpenIntObjectHashMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap; -import it.unimi.dsi.fastutil.ints.IntAVLTreeSet; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntIterator; -import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectAVLTreeSet; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectBigArrayBigList; -import it.unimi.dsi.fastutil.objects.ObjectBigList; -import it.unimi.dsi.fastutil.objects.ObjectList; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import org.gephi.graph.api.types.TimestampSet; - -import java.util.ArrayList; -import java.util.BitSet; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Random; - -/** - * - * @author mbastian - */ -public class DataStructureBenchmark { - - private static int NODES = 500000; - private static int LOW_NODES = 50000; - private Object object; - - /** - * Insertion and memory usage for Int2ObjectOpenHashMap - */ - public Runnable openHashMapMemory() { - return () -> { - int nodes = NODES; - Int2ObjectMap map = new Int2ObjectOpenHashMap(nodes); - for (int i = 0; i < nodes; i++) { - map.put(i, new Object()); - } - object = map; - }; - } - - /** - * Insertion and memory usage for Int2ObjectOpenHashMap without original capcity - */ - public Runnable dynamicOpenHashMapMemory() { - return () -> { - int nodes = NODES; - Int2ObjectMap map = new Int2ObjectOpenHashMap(); - for (int i = 0; i < nodes; i++) { - map.put(i, new Object()); - } - object = map; - }; - } - - /** - * Insertion and memory usage for Int2ObjectRBTreeMap - */ - public Runnable rbHashMapMemory() { - return () -> { - int nodes = NODES; - Int2ObjectMap map = new Int2ObjectRBTreeMap(); - for (int i = 0; i < nodes; i++) { - map.put(i, new Object()); - } - object = map; - }; - } - - /** - * Insertion and memory usage for OpenIntObjectHashMap - */ - public Runnable coltOpenHashMapMemory() { - return () -> { - int nodes = NODES; - OpenIntObjectHashMap map = new OpenIntObjectHashMap(nodes); - for (int i = 0; i < nodes; i++) { - map.put(i, new Object()); - } - object = map; - }; - } - - /** - * Insertion and memory usage for basic array - */ - public Runnable arrayMemory() { - return () -> { - int nodes = NODES; - Object[] array = new Object[nodes]; - for (int i = 0; i < nodes; i++) { - array[i] = new Object(); - } - object = array; - }; - } - - /** - * Insertion and memory usage for object list - */ - public Runnable objectListMemory() { - return () -> { - int nodes = NODES; - final ObjectList list = new ObjectArrayList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - object = list; - }; - } - - public Runnable dynamicObjectListMemory() { - return () -> { - final ObjectList list = new ObjectArrayList(); - int nodes = NODES; - for (int i = 0; i < nodes; i++) { - list.add(1); - } - object = list; - }; - } - - /** - * Insertion and memory usage for object list - */ - public Runnable objectBigListMemory() { - return () -> { - int nodes = NODES; - final ObjectBigList list = new ObjectBigArrayBigList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - object = list; - }; - } - - public Runnable openHashMapIteration() { - int nodes = NODES; - final Int2ObjectMap map = new Int2ObjectOpenHashMap(nodes); - for (int i = 0; i < nodes; i++) { - map.put(i, new Integer(1)); - } - return () -> { - int sum = 0; - for (Object i : map.values()) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable rbHashMapIteration() { - int nodes = NODES; - final Int2ObjectMap map = new Int2ObjectRBTreeMap(); - for (int i = 0; i < nodes; i++) { - map.put(i, new Integer(1)); - } - return () -> { - int sum = 0; - for (Object i : map.values()) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable arrayIteration() { - // Create array - int nodes = NODES; - final Object[] array = new Object[nodes]; - for (int i = 0; i < nodes; i++) { - array[i] = 1; - } - return () -> { - int sum = 0; - for (Object i : array) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable linkedListIteration() { - // Create array - int nodes = NODES; - final LinkedList list = new LinkedList(); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectListIteration() { - int nodes = NODES; - final ObjectList list = new ObjectArrayList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectBigListIteration() { - int nodes = NODES; - final ObjectBigList list = new ObjectBigArrayBigList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(1); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectLinkedHashMapIteration() { - int nodes = NODES; - final Int2ObjectLinkedOpenHashMap list = new Int2ObjectLinkedOpenHashMap(nodes); - for (int i = 0; i < nodes; i++) { - list.put(new Integer(i), new Integer(1)); - } - return () -> { - int sum = 0; - for (Object i : list.values()) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectHashObjectSet() { - int nodes = NODES; - final ObjectOpenHashSet list = new ObjectOpenHashSet(nodes); - for (int i = 0; i < nodes; i++) { - list.add(i); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable objectAVLObjectSet() { - int nodes = NODES; - final ObjectAVLTreeSet list = new ObjectAVLTreeSet(); - for (int i = 0; i < nodes; i++) { - list.add(i); - } - return () -> { - int sum = 0; - for (Object i : list) { - sum += (Integer) i; - } - object = sum; - }; - } - - public Runnable openHashMapResetAll() { - int nodes = LOW_NODES; - final Int2IntOpenHashMap map = new Int2IntOpenHashMap(nodes); - for (int i = 0; i < nodes; i++) { - map.put(i, i); - } - return () -> { - int nodes1 = LOW_NODES; - for (int i = 0; i < nodes1; i++) { - map.put(i, i); - } - }; - } - - public Runnable arrayResetAll() { - int nodes = LOW_NODES; - final int[] array = new int[nodes]; - for (int i = 0; i < nodes; i++) { - array[i] = i; - } - return () -> { - int nodes1 = LOW_NODES; - for (int i = 0; i < nodes1; i++) { - array[i] = i; - } - }; - } - - public Runnable objectListResetAll() { - int nodes = LOW_NODES; - final IntList list = new IntArrayList(nodes); - for (int i = 0; i < nodes; i++) { - list.add(i); - } - return () -> { - int nodes1 = LOW_NODES; - for (int i = 0; i < nodes1; i++) { - list.set(i, i); - } - }; - } - - public Runnable fastutilObject2IntIterate() { - int nodes = NODES; - final Object2IntMap map = new Object2IntOpenHashMap<>(); - for (int i = 0; i < nodes; i++) { - map.put("" + i, i); - } - final Random rand = new Random(456); - return () -> { - int matches = 0; - for (int i = 0; i < 50000; i++) { - int id = rand.nextInt(NODES - 1); - if (map.containsKey("" + id)) { - matches += id; - } - } - object = matches; - }; - } - - public Runnable javaObject2IntIterate() { - int nodes = NODES; - final Map map = new HashMap<>(); - for (int i = 0; i < nodes; i++) { - map.put("" + i, i); - } - final Random rand = new Random(456); - return () -> { - int matches = 0; - for (int i = 0; i < 50000; i++) { - int id = rand.nextInt(NODES - 1); - if (map.containsKey("" + id)) { - matches += id; - } - } - object = matches; - }; - } - - public Runnable javaArrayIterate() { - final boolean[] bitset = new boolean[NODES]; - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - bitset[i] = rand.nextBoolean(); - } - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset[i]; - if (b) { - cardinality++; - } - } - object = cardinality; - }; - } - - public Runnable javaBitVectorIterate() { - final BitSet bitset = new BitSet(NODES); - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - bitset.set(i, rand.nextBoolean()); - } - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset.get(i); - if (b) { - cardinality++; - } - } - object = cardinality; - }; - } - - public Runnable coltBitVector() { - final BitVector bitset = new BitVector(NODES); - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - if (rand.nextBoolean()) { - bitset.set(i); - } - } - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset.get(i); - if (b) { - cardinality++; - } - } - object = cardinality; - }; - } - - public Runnable coltBitVectorIterateAndMapLookup() { - final BitVector bitset = new BitVector(NODES); - final Int2IntOpenHashMap map = new Int2IntOpenHashMap(); - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - if (rand.nextBoolean()) { - bitset.set(i); - map.put(i, i); - } - } - map.trim(); - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset.get(i); - if (b) { - long rank = map.get(i); - cardinality += rank; - } - } - object = cardinality; - }; - } - - public Runnable javaBitVectorMemory() { - - return () -> { - BitSet bitset = new BitSet(10000000); - Random rand = new Random(23); - for (int i = 0; i < 10000000; i++) { - bitset.set(i, rand.nextBoolean()); - } - object = bitset; - }; - } - - public Runnable coltBitVectorMemory() { - - return () -> { - BitVector bitset = new BitVector(10000000); - Random rand = new Random(23); - for (int i = 0; i < 10000000; i++) { - if (rand.nextBoolean()) { - bitset.set(i); - } - } - object = bitset; - }; - } - - public Runnable sparseArrayIterate(float emptyRatio) { - final BitVector bitset = new BitVector(NODES); - final int[] values = new int[NODES]; - Random rand2 = new Random(456); - Random rand = new Random(23); - for (int i = 0; i < NODES; i++) { - if (rand.nextDouble() < emptyRatio) { - bitset.set(i); - } - values[i] = rand2.nextInt(NODES); - } - return () -> { - int cardinality = 0; - for (int i = 0; i < NODES; i++) { - boolean b = bitset.get(i); - if (b) { - int val = values[i]; - cardinality += val; - } - } - object = cardinality; - }; - } - - public Runnable intHashSetIterate(float emptyRatio) { - int nodes = (int) (NODES * (1 - emptyRatio)); - Random rand2 = new Random(456); - final IntSet values = new IntOpenHashSet(nodes); - for (int i = 0; i < nodes; i++) { - values.add(rand2.nextInt(NODES)); - } - return () -> { - int cardinality = 0; - IntIterator itr = values.iterator(); - while (itr.hasNext()) { - int i = itr.nextInt(); - cardinality += i; - } - object = cardinality; - }; - } - - public Runnable intAVLSetIterate(float emptyRatio) { - int nodes = (int) (NODES * (1 - emptyRatio)); - Random rand2 = new Random(456); - final IntSet values = new IntAVLTreeSet(); - for (int i = 0; i < nodes; i++) { - values.add(rand2.nextInt(NODES)); - } - return () -> { - int cardinality = 0; - IntIterator itr = values.iterator(); - while (itr.hasNext()) { - int i = itr.nextInt(); - cardinality += i; - } - object = cardinality; - }; - } - - public Runnable arraySetMemory() { - final int size = 10000; - final int timestamps = 100; - - return () -> { - List trees = new ArrayList<>(); - Random rand = new Random(1234); - for (int i = 0; i < size; i++) { - TimestampSet set = new TimestampSet(timestamps); - for (int j = 0; j < timestamps; j++) { - final double val = rand.nextInt(timestamps); - set.add(val); - trees.add(set); - } - } - object = trees; - }; - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java deleted file mode 100644 index 654db3ed..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/EdgeStoreBenchmark.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.gephi.graph.benchmark.benchmarks; - -import java.util.Iterator; -import java.util.List; - -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.Node; -import org.gephi.graph.impl.EdgeStore; - -/** - * - * @author mbastian, niteshbhargv - */ -public class EdgeStoreBenchmark { - - private Object object; - - public Runnable pushEdgeStore(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - final List nodeList = graph.getNodes(); - final List edgeList = graph.getEdges(); - graph.getStore().addAllNodes(nodeList); - - Runnable runnable = () -> { - edgeStore.clear(); - for (Edge edge : edgeList) { - edgeStore.add(edge); - } - }; - return runnable; - } - - public Runnable iterateEdgeStore(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - - Runnable runnable = () -> { - Iterator itr = edgeStore.iterator(); - for (; itr.hasNext();) { - object = itr.next(); - } - }; - return runnable; - } - - public Runnable iterateEdgeStoreNeighborsOut(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - final List nodeList = graph.getNodes(); - - Runnable runnable = () -> { - for (Node node : nodeList) { - Iterator itr = edgeStore.edgeOutIterator(node); - for (; itr.hasNext();) { - object = itr.next(); - } - } - }; - return runnable; - } - - public Runnable iterateEdgeStoreNeighborsInOut(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - final List nodeList = graph.getNodes(); - - Runnable runnable = () -> { - for (Node node : nodeList) { - Iterator itr = edgeStore.edgeIterator(node); - for (; itr.hasNext();) { - object = itr.next(); - } - } - }; - return runnable; - } - - public Runnable resetEdgeStore(int nodes, double prob) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, prob, config).generate().commit(); - final EdgeStore edgeStore = graph.getStore().getEdgeStore(); - final List edgeList = graph.getEdges(); - - Runnable runnable = () -> { - for (Edge e : edgeList) { - edgeStore.remove(e); - } - for (Edge e : edgeList) { - edgeStore.add(e); - } - }; - return runnable; - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java deleted file mode 100644 index bd410810..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/Generator.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.gephi.graph.benchmark.benchmarks; - -import java.util.ArrayList; -import java.util.List; - -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.GraphFactory; -import org.gephi.graph.api.Node; -import org.gephi.graph.impl.GraphModelImpl; -import org.gephi.graph.impl.GraphStore; - -public abstract class Generator { - - protected final GraphFactory factory; - protected final GraphStore graphStore; - protected List nodes; - protected List edges; - - public Generator() { - this(new Configuration()); - } - - public Generator(final Configuration config) { - GraphModelImpl model = new GraphModelImpl(config); - factory = model.factory(); - graphStore = model.getStore(); - nodes = new ArrayList<>(); - edges = new ArrayList<>(); - } - - public GraphStore getStore() { - return graphStore; - } - - public List getNodes() { - return nodes; - } - - public List getEdges() { - return edges; - } - - public void clean() { - nodes = null; - edges = null; - } - - public abstract Generator generate(); - - public abstract Generator commit(); - - protected void commitInner() { - for (Node node : nodes) { - graphStore.addNode(node); - } - for (Edge edge : edges) { - graphStore.addEdge(edge); - } - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java deleted file mode 100644 index 5c40661d..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/KleinbergGraph.java +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark.benchmarks; - -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.Node; - -import java.util.Random; - -/** - * Generates a directed connected graph. - * - * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.117.7097&rep=rep1&type=pdf - * http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.83.381&rep=rep1&type=pdf - * - * n >= 2 p >= 1 p <= 2n - 2 q >= 0 q <= n^2 - p * (p + 3) / 2 - 1 for p < n q - * <= (2n - p - 3) * (2n - p) / 2 + 1 for p >= n r >= 0 - * - * Ω(n^4 * q) - * - * @author Nitesh Bhargava - */ -public class KleinbergGraph extends Generator { - - private int n = 5; - private int p = 2; - private int q = 2; - private int r = 0; - private boolean torusBased; - - /** - * User defined Kleinberg Graph no*no = number of nodes local = local contacts - * Long = long range contacts - */ - KleinbergGraph(int no, int local, int longRange) { - super(); - n = no; - p = local; - q = longRange; - r = 0; - torusBased = false; - } - - @Override - public KleinbergGraph generate() { - Random random = new Random(); - - // Creating lattice n x n - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - Node node = factory.newNode(); - nodes.add(node); - } - } - LongOpenHashSet edgeSet = new LongOpenHashSet(); - - // Creating edges from each node to p local contacts - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - for (int k = i - p; k <= i + p; ++k) { - for (int l = j - p; l <= j + p; ++l) { - if ((isTorusBased() || !isTorusBased() && k >= 0 && k < n && l >= 0 && l < n) && d(i, j, k, l) <= p && nodes - .get(i * n + j) != nodes.get(((k + n) % n) * n + ((l + n) % n))) { - Edge edge = factory.newEdge(nodes.get(i * n + j), nodes - .get(((k + n) % n) * n + ((l + n) % n)), 0, true); - Object id = edge.getId(); - if (id instanceof Number) { - edges.add(edge); - edgeSet.add(((Number) id).longValue()); - } - } - } - } - } - } - - // Creating edges from each node to q long-range contacts - for (int i = 0; i < n; ++i) { - for (int j = 0; j < n; ++j) { - double sum = 0.0; - for (int k = 0; k < n; ++k) { - for (int l = 0; l < n; ++l) { - if (!isTorusBased() && d(i, j, k, l) > p) { - sum += Math.pow(d(i, j, k, l), -r); - } else if (isTorusBased() && dtb(i, j, k, l) > p) { - sum += Math.pow(dtb(i, j, k, l), -r); - } - - } - } - for (int m = 0; m < q; ++m) { - double b = random.nextDouble(); - boolean e = false; - while (!e) { - double pki = 0.0; - for (int k = 0; k < n && !e; ++k) { - for (int l = 0; l < n && !e; ++l) { - if (!isTorusBased() && d(i, j, k, l) > p || isTorusBased() && dtb(i, j, k, l) > p) { - pki += Math.pow(!isTorusBased() ? d(i, j, k, l) : dtb(i, j, k, l), -r) / sum; - Edge edge = factory.newEdge(nodes.get(i * n + j), nodes.get(k * n + l), 0, true); - Object id = edge.getId(); - if (id instanceof Number) { - if (b <= pki && !edgeSet.contains(((Number) id).longValue())) { - edges.add(edge); - edgeSet.add(((Number) id).longValue()); - e = true; - } - } - } - } - } - b = random.nextDouble(); - } - - } - } - } - return this; - } - - @Override - public KleinbergGraph commit() { - commitInner(); - return this; - } - - private int d(int i, int j, int k, int l) { - return Math.abs(k - i) + Math.abs(l - j); - } - - private int dtb(int i, int j, int k, int l) { - return Math.min(Math.abs(k - i), n - Math.abs(k - i)) + Math.min(Math.abs(l - j), n - Math.abs(l - j)); - } - - public int getn() { - return n; - } - - public int getp() { - return p; - } - - public int getq() { - return q; - } - - public int getr() { - return r; - } - - public boolean isTorusBased() { - return torusBased; - } - - public void setn(int n) { - this.n = n; - } - - public void setp(int p) { - this.p = p; - } - - public void setq(int q) { - this.q = q; - } - - public void setr(int r) { - this.r = r; - } - - public void setTorusBased(boolean torusBased) { - this.torusBased = torusBased; - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java deleted file mode 100644 index c82dfa27..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/LockingBenchmark.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark.benchmarks; - -import java.util.Random; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class LockingBenchmark { - - private final DataStruture struture = new DataStruture(); - private double number; - private final int READS = 500; - private final int WRITES = 100; - private final int READER_THREADS = 4; - private final int WRITER_THREADS = 4; - - public Runnable readWithoutLock() { - return () -> { - for (int i = 0; i < READS; i++) { - struture.read(); - } - }; - } - - public Runnable readWithLock() { - final ReadWriteLock lock = new ReentrantReadWriteLock(true); - return () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - } - - public Runnable fairReadOnly() { - final ReadWriteLock lock = new ReentrantReadWriteLock(true); - return () -> { - Runnable r = () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(r); - thread.start(); - threads[i] = thread; - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable unfairReadOnly() { - final ReadWriteLock lock = new ReentrantReadWriteLock(false); - return () -> { - Runnable r = () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(r); - thread.start(); - threads[i] = thread; - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable fairWriteOnly() { - final ReadWriteLock lock = new ReentrantReadWriteLock(true); - return () -> { - Runnable r = () -> { - for (int i = 0; i < WRITES; i++) { - lock.writeLock().lock(); - struture.write(); - lock.writeLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(r); - thread.start(); - threads[i] = thread; - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable unfairWriteOnly() { - final ReadWriteLock lock = new ReentrantReadWriteLock(false); - return () -> { - Runnable r = () -> { - for (int i = 0; i < WRITES; i++) { - lock.writeLock().lock(); - struture.write(); - lock.writeLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(r); - thread.start(); - threads[i] = thread; - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable fairReadWrites() { - final ReadWriteLock lock = new ReentrantReadWriteLock(true); - return () -> { - Runnable reader = () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - Runnable writer = () -> { - for (int i = 0; i < WRITES; i++) { - lock.writeLock().lock(); - struture.write(); - lock.writeLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS + WRITER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(reader); - threads[i] = thread; - } - for (int i = 0; i < WRITER_THREADS; i++) { - Thread thread = new Thread(writer); - threads[READER_THREADS + i] = thread; - } - for (Thread thread : threads) { - thread.start(); - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - public Runnable unfairReadWrites() { - final ReadWriteLock lock = new ReentrantReadWriteLock(false); - return () -> { - Runnable reader = () -> { - for (int i = 0; i < READS; i++) { - lock.readLock().lock(); - struture.read(); - lock.readLock().unlock(); - } - }; - Runnable writer = () -> { - for (int i = 0; i < WRITES; i++) { - lock.writeLock().lock(); - struture.write(); - lock.writeLock().unlock(); - } - }; - Thread[] threads = new Thread[READER_THREADS + WRITER_THREADS]; - for (int i = 0; i < READER_THREADS; i++) { - Thread thread = new Thread(reader); - threads[i] = thread; - } - for (int i = 0; i < WRITER_THREADS; i++) { - Thread thread = new Thread(writer); - threads[READER_THREADS + i] = thread; - } - for (Thread thread : threads) { - thread.start(); - } - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException ex) { - Logger.getLogger(LockingBenchmark.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - private class DataStruture { - - private final int[] values = new int[10000]; - private final int readLoops = 10; - private final int writeLoops = 5; - - public DataStruture() { - Random rand = new Random(454); - for (int i = 0; i < values.length; i++) { - values[i] = rand.nextInt(values.length); - } - } - - public void write() { - Random rand = new Random(45445); - for (int i = 0; i < writeLoops; i++) { - for (int j = 0; j < values.length; j++) { - values[j] = rand.nextInt(values.length); - } - } - } - - public void read() { - double avg = 0; - for (int i = 0; i < readLoops; i++) { - double sum = 0; - for (int j = 0; j < values.length; j++) { - sum += values[j]; - } - sum /= values.length; - avg += sum; - } - avg /= readLoops; - number = avg; - } - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java deleted file mode 100644 index 5aee547b..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/NodeStoreBenchmark.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark.benchmarks; - -import java.util.Iterator; -import java.util.List; - -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Node; -import org.gephi.graph.impl.NodeImpl; -import org.gephi.graph.impl.NodeStore; - -/** - * - * @author mbastian, niteshbhargv - */ -public class NodeStoreBenchmark { - - private Object object; - - public Runnable iterateStore(final int nodes) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, 0, config).generate().commit(); - final NodeStore nodeStore = graph.getStore().getNodeStore(); - Runnable runnable = () -> { - Iterator m = nodeStore.iterator(); - for (; m.hasNext();) { - NodeImpl b = (NodeImpl) m.next(); - object = b; - } - }; - return runnable; - } - - public Runnable resetNodeStore(final int nodes) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, 0, config).generate().commit(); - final NodeStore nodeStore = graph.getStore().getNodeStore(); - final List nodeList = graph.getNodes(); - Runnable runnable = () -> { - for (Node n : nodeList) { - nodeStore.remove(n); - } - for (Node n : nodeList) { - nodeStore.add(n); - } - }; - return runnable; - } - - public Runnable pushStore(int nodes) { - final Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); - config.setNodeIdType(Integer.class); - final RandomGraph graph = new RandomGraph(nodes, 0, config).generate(); - final NodeStore nodeStore = graph.getStore().getNodeStore(); - final List nodeList = graph.getNodes(); - - Runnable runnable = () -> { - nodeStore.clear(); - for (Node n : nodeList) { - nodeStore.add(n); - } - }; - return runnable; - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java b/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java deleted file mode 100644 index 4eb1f5ab..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/benchmarks/RandomGraph.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark.benchmarks; - -import java.util.Random; - -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.Node; -import org.gephi.graph.impl.NodeImpl; - -/** - * Generates directed connected random graph with wiring probability p - * - * @author Mathieu Bastian, Nitesh Bhargava - */ -public class RandomGraph extends Generator { - - protected final int numberOfNodes; - protected final int numberOfEdges; - protected final double wiringProbability; - - public RandomGraph(int n, double p) { - this(n, p, new Configuration()); - } - - public RandomGraph(int n, double p, Configuration config) { - super(config); - numberOfNodes = n; - numberOfEdges = (int) (n * (n - 1) * p); - wiringProbability = p; - } - - public RandomGraph(int nodes, int edges) { - this(nodes, edges, new Configuration()); - } - - public RandomGraph(int nodes, int edges, Configuration confi) { - this(nodes, ((double) edges) / (nodes * (nodes - 1)), confi); - } - - @Override - public RandomGraph generate() { - Random random = new Random(); - - for (int i = 0; i < numberOfNodes; i++) { - Node node = factory.newNode(i); - nodes.add(node); - } - - if (wiringProbability > 0) { - for (int i = 0; i < numberOfNodes - 1; i++) { - NodeImpl source = graphStore.getNode(i); - for (int j = i + 1; j < numberOfNodes; j++) { - NodeImpl target = graphStore.getNode(j); - - if (random.nextDouble() < wiringProbability && source != target) { - Edge edge = factory.newEdge(source, target, 0, true); - edges.add(edge); - } - } - } - } - return this; - } - - @Override - public RandomGraph commit() { - commitInner(); - return this; - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java b/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java deleted file mode 100644 index 077d541a..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/nanobench/NanoBench.java +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark.nanobench; - -import java.lang.management.ManagementFactory; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Lightweight CPU and memory benchmarking utility. - *

- * Inspired from nanobench (http://code.google.com/p/nanobench/) - * - * @author mbastian - */ -public class NanoBench { - - public static NanoBench create() { - return new NanoBench(); - } - - private static final Logger logger = Logger.getLogger(NanoBench.class.getSimpleName()); - private int numberOfMeasurement = 50; - private int numberOfWarmUp = 0; - private List listeners; - - public NanoBench() { - listeners = new ArrayList<>(2); - listeners.add(new CPUMeasure(logger)); - listeners.add(new MemoryUsage(logger)); - } - - public NanoBench measurements(int numberOfMeasurement) { - this.numberOfMeasurement = numberOfMeasurement; - return this; - } - - public NanoBench warmUps(int numberOfWarmups) { - this.numberOfWarmUp = numberOfWarmups; - return this; - } - - public NanoBench cpuAndMemory() { - listeners = new ArrayList<>(2); - listeners.add(new CPUMeasure(logger)); - listeners.add(new MemoryUsage(logger)); - return this; - } - - public static Logger getLogger() { - return logger; - } - - public NanoBench cpuOnly() { - listeners = new ArrayList<>(1); - listeners.add(new CPUMeasure(logger)); - return this; - } - - public NanoBench memoryOnly() { - listeners = new ArrayList<>(1); - listeners.add(new MemoryUsage(logger)); - return this; - } - - public void measure(String label, Runnable task) { - MemoryUtil.restoreJvm(); - doWarmup(task); - MemoryUtil.restoreJvm(); - stress(); - doMeasure(label, task); - stress(); - MemoryUtil.restoreJvm(); - try { - Thread.sleep(1000); - } catch (InterruptedException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - static int[] arrayStress = new int[10000]; - - private void stress() { - int m = 0; - for (int j = 0; j < 100; j++) { - int dummy = 0; - for (int i = 1; i < arrayStress.length; i++) { - arrayStress[i] = (int) Math.round(Math.log(i)); - dummy += arrayStress[i - 1]; - } - m += dummy; - } - } - - private void doMeasure(String label, Runnable task) { - for (int i = 0; i < this.numberOfMeasurement; i++) { - TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState(label, i, this.numberOfMeasurement), task, - listeners); - tmp.run(); - } - } - - private void doWarmup(Runnable task) { - for (int i = 0; i < this.numberOfWarmUp; i++) { - TimeMeasureProxy tmp = new TimeMeasureProxy(new MeasureState("_warmup_", i, this.numberOfWarmUp), task, - listeners); - tmp.run(); - } - } - - /** - * Decorated runnable which enables measurements. - */ - private static class TimeMeasureProxy implements Runnable { - - private MeasureState state; - private Runnable runnable; - private List listeners; - - public TimeMeasureProxy(MeasureState state, Runnable runnable, List listeners) { - super(); - this.state = state; - this.runnable = runnable; - this.listeners = listeners; - } - - @Override - public void run() { - this.state.startNow(); - this.runnable.run(); - this.state.endNow(); - if (!state.getLabel().equals("_warmup_")) { - notifyMeasurement(state); - } - } - - private void notifyMeasurement(MeasureState times) { - for (MeasureListener listener : this.listeners) { - listener.onMeasure(times); - } - } - } - - /** - * Interface for measure listeners. Measure listeners are called when a - * measurement is finished. - */ - private interface MeasureListener { - - void onMeasure(MeasureState state); - } - - /** - * Basic class to measure time spent in each measurement - */ - private static class MeasureState implements Comparable { - - private String label; - private long startTime; - private long endTime; - private long index; - private int measurement; - - public MeasureState(String label, long index, int measurement) { - super(); - this.label = label; - this.measurement = measurement; - this.index = index; - } - - public long getIndex() { - return index; - } - - public String getLabel() { - return label; - } - - public long getStartTime() { - return startTime; - } - - public long getEndTime() { - return endTime; - } - - public long getMeasurements() { - return measurement; - } - - public long getMeasureTime() { - return endTime - startTime; - } - - public void startNow() { - this.startTime = System.nanoTime(); - } - - public void endNow() { - this.endTime = System.nanoTime(); - } - - @Override - public int compareTo(MeasureState another) { - if (this.startTime > another.startTime) { - return -1; - } else if (this.startTime < another.startTime) { - return 1; - } else { - return 0; - } - } - } - - /** - * CPU time listener to calculate the average time spent in a measurement. - *

- * The listener is called at the end of each measurement and collect the time - * spent from the MeasureState instance. At the last measurement it - * shows the average time spent, the total time and the number of measurement - * per seconds. - */ - private static class CPUMeasure implements MeasureListener { - - private static final double BY_SECONDS = 1000000000.0; - private final Logger log; - private static final DecimalFormat decimalFormat = new DecimalFormat("#,##0.0000"); - private static final DecimalFormat integerFormat = new DecimalFormat("#,##0.0"); - private int count = 0; - private long timeUsed = 0; - - public CPUMeasure(Logger logger) { - this.log = logger; - } - - @Override - public void onMeasure(MeasureState state) { - count++; - outputMeasureInfo(state); - } - - private void outputMeasureInfo(MeasureState state) { - timeUsed += state.getMeasureTime(); - - if (isEnd(state)) { - long total = timeUsed; - - StringBuilder sb = new StringBuilder("\n"); - sb.append(state.getLabel()).append("\t").append("avg: ") - .append(decimalFormat.format(total / state.getMeasurements() / 1000000.0)).append(" ms\t") - .append("total: ").append(integerFormat.format(total / 1000000000.0)).append(" s\t") - .append(" tps: ").append(integerFormat.format(state.getMeasurements() / (total / BY_SECONDS))) - .append("\t").append("running: ").append(count).append(" times"); - count = 0; - timeUsed = 0; - if (!state.getLabel().equals("_warmup_")) { - log.info(sb.toString()); - } - } - } - - private boolean isEnd(MeasureState state) { - return count == state.getMeasurements(); - } - } - - /** - * Memory usage listener to calculate the average memory usage. - *

- * The listener is called after each measurement and perform a full GC and - * calculate free memory. At the last measurement it shows the average memory - * usage. - */ - private static class MemoryUsage implements MeasureListener { - - private final Logger log; - private static final DecimalFormat integerFormat = new DecimalFormat("#,##0.000"); - private int count = 0; - private long memoryUsed = 0; - - public MemoryUsage(Logger logger) { - this.log = logger; - } - - @Override - public void onMeasure(MeasureState state) { - count++; - outputMeasureInfo(state); - } - - private void outputMeasureInfo(MeasureState state) { - MemoryUtil.restoreJvm(); - memoryUsed += MemoryUtil.memoryUsed(); - - if (isEnd(state)) { - StringBuilder sb = new StringBuilder("\n"); - sb.append("memory-usage: ").append(state.getLabel()).append("\t") - .append(format((memoryUsed / count) / (1024.0 * 1024.0))).append(" Mb\n"); - count = 0; - memoryUsed = 0; - - if (!state.getLabel().equals("_warmup_")) { - log.info(sb.toString()); - } - } - } - - private String format(double value) { - return integerFormat.format(value); - } - - private boolean isEnd(MeasureState state) { - return count == state.getMeasurements(); - } - } - - /** - * Utility memory class to perform GC and calculate memory usage - */ - public static class MemoryUtil { - - /** - * Call GC until no more memory can be freed - */ - public static void restoreJvm() { - int maxRestoreJvmLoops = 10; - long memUsedPrev = memoryUsed(); - for (int i = 0; i < maxRestoreJvmLoops; i++) { - System.runFinalization(); - System.gc(); - - long memUsedNow = memoryUsed(); - // break early if have no more finalization and get constant mem used - if ((ManagementFactory.getMemoryMXBean() - .getObjectPendingFinalizationCount() == 0) && (memUsedNow >= memUsedPrev)) { - break; - } else { - memUsedPrev = memUsedNow; - } - } - } - - /** - * Return the memory used in bytes - * - * @return heap memory used in bytes - */ - public static long memoryUsed() { - Runtime rt = Runtime.getRuntime(); - return rt.totalMemory() - rt.freeMemory(); - } - } -} diff --git a/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java b/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java deleted file mode 100644 index d1a166c2..00000000 --- a/src/benchmark/java/org/gephi/graph/benchmark/util/ReporterHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012-2013 Gephi Consortium - * - * Licensed 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.gephi.graph.benchmark.util; - -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import org.testng.Reporter; - -/** - * Bridge between Java Logging API and TestNG's Reporter - * - * @author mbastian - */ -public class ReporterHandler extends Handler { - - @Override - public void publish(LogRecord record) { - String prefix = ""; - if (record.getLevel().equals(Level.INFO)) { - prefix = "[INFO] "; - } else if (record.getLevel().equals(Level.WARNING)) { - prefix = "[WARN] "; - } else if (record.getLevel().equals(Level.SEVERE)) { - prefix = "[SEVERE] "; - } - Reporter.log(prefix + record.getMessage(), true); - } - - @Override - public void flush() { - } - - @Override - public void close() throws SecurityException { - } -} From 1f5dbdc88aeb82e860cf346afe20e0e0dcd2ecc9 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 6 Apr 2023 21:58:45 +0200 Subject: [PATCH 120/271] Remove Joda Time dependency (#163) * Original commit from Julien G. * Still use IllegalArgumentException were it used to be and cleanup * Update to 0.7.0-SNAPSHOT * Removed Joda dependency. Co-authored-by: Clement Levallois <1244100+seinecle@users.noreply.github.com> Co-authored-by: Julien Gouesse --- README.md | 2 +- pom.xml | 7 +- .../org/gephi/graph/api/AttributeUtils.java | 166 +++++++++++------- .../java/org/gephi/graph/api/GraphModel.java | 6 +- .../gephi/graph/api/types/IntervalMap.java | 4 +- .../gephi/graph/api/types/IntervalSet.java | 4 +- .../org/gephi/graph/api/types/TimeMap.java | 4 +- .../org/gephi/graph/api/types/TimeSet.java | 4 +- .../gephi/graph/api/types/TimestampMap.java | 4 +- .../gephi/graph/api/types/TimestampSet.java | 4 +- .../graph/impl/FormattingAndParsingUtils.java | 9 +- .../org/gephi/graph/impl/GraphModelImpl.java | 6 +- .../java/org/gephi/graph/impl/GraphStore.java | 4 +- .../graph/impl/GraphStoreConfiguration.java | 5 +- .../org/gephi/graph/impl/IntervalsParser.java | 33 ++-- .../org/gephi/graph/impl/Serialization.java | 14 +- .../gephi/graph/impl/TimestampsParser.java | 31 ++-- .../graph/api/types/IntervalMapTest.java | 23 +-- .../graph/api/types/IntervalSetTest.java | 27 +-- .../graph/api/types/TimestampMapTest.java | 24 +-- .../graph/api/types/TimestampSetTest.java | 23 +-- .../gephi/graph/impl/AttributeUtilsTest.java | 86 +++++---- .../org/gephi/graph/impl/GraphModelTest.java | 12 +- .../gephi/graph/impl/IntervalsParserTest.java | 3 +- .../gephi/graph/impl/SerializationTest.java | 8 +- .../graph/impl/TimestampsParserTest.java | 3 +- 26 files changed, 288 insertions(+), 228 deletions(-) diff --git a/README.md b/README.md index 351412d8..c99bebd7 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ API Documentation is available [here](https://www.javadoc.io/doc/org.gephi/graph ## Dependencies -GraphStore depends on FastUtil >= 6.0, Colt 1.2.0 and Joda-Time 2.2. +GraphStore depends on FastUtil >= 6.0 and Colt 1.2.0. For a complete list of dependencies, consult the `pom.xml` file. diff --git a/pom.xml b/pom.xml index 07d5505e..abb2e93e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.6.15-SNAPSHOT + 0.7.0-SNAPSHOT jar GraphStore @@ -72,11 +72,6 @@ colt 1.2.0 - - joda-time - joda-time - 2.12.2 - diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index fefd1bb0..365c6544 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -41,6 +41,7 @@ import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; import it.unimi.dsi.fastutil.shorts.ShortArrayList; import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; +import java.time.format.DateTimeParseException; import org.gephi.graph.impl.TimestampsParser; import org.gephi.graph.impl.IntervalsParser; import org.gephi.graph.impl.FormattingAndParsingUtils; @@ -60,6 +61,13 @@ import java.math.BigInteger; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -82,9 +90,6 @@ import org.gephi.graph.api.types.TimeSet; import org.gephi.graph.impl.ArraysParser; import org.gephi.graph.impl.GraphStoreConfiguration; -import org.joda.time.DateTimeZone; -import org.joda.time.format.DateTimeFormatter; -import org.joda.time.format.ISODateTimeFormat; /** * Set of utility methods to manipulate supported attribute types. @@ -105,9 +110,9 @@ public class AttributeUtils { // These are used to avoid creating a lot of new instances of // DateTimeFormatter - private static final Map DATE_PRINTERS_BY_TIMEZONE; - private static final Map DATE_TIME_PRINTERS_BY_TIMEZONE; - private static final Map DATE_TIME_PARSERS_BY_TIMEZONE; + private static final Map DATE_PRINTERS_BY_TIMEZONE; + private static final Map DATE_TIME_PRINTERS_BY_TIMEZONE; + private static final Map DATE_TIME_PARSERS_BY_TIMEZONE; // Collectio types to speedup lookup private static final Set TYPED_LIST_TYPES; @@ -220,10 +225,26 @@ public class AttributeUtils { TYPES_STANDARDIZATION = Collections.unmodifiableMap(typesStandardization); // Datetime - make sure UTC timezone is used by default - DATE_TIME_PARSER = ISODateTimeFormat.dateOptionalTimeParser() + DATE_TIME_PARSER = new DateTimeFormatterBuilder().parseCaseInsensitive() + .appendOptional(DateTimeFormatter.ISO_DATE).appendOptional(DateTimeFormatter.ofPattern("yyyyMMdd")) + .optionalStart().appendLiteral('T').append(DateTimeFormatter.ISO_TIME) + .appendPattern("[.SSSSSSSSS][.SSSSSS][.SSS]").optionalEnd().optionalStart() + .appendFraction(ChronoField.NANO_OF_SECOND, 9, 9, true).optionalEnd() + // optional nanos with 6 digits (including decimal point) + .optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 6, 6, true).optionalEnd() + // optional nanos with 3 digits (including decimal point) + .optionalStart().appendFraction(ChronoField.NANO_OF_SECOND, 3, 3, true).optionalEnd() + .parseDefaulting(ChronoField.HOUR_OF_DAY, 0).parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0).parseDefaulting(ChronoField.NANO_OF_SECOND, 0) + .toFormatter().withZone(GraphStoreConfiguration.DEFAULT_TIME_ZONE); + DATE_PRINTER = new DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern("yyyy-MM-dd").toFormatter() + .withZone(GraphStoreConfiguration.DEFAULT_TIME_ZONE); + DATE_TIME_PRINTER = new DateTimeFormatterBuilder().parseCaseInsensitive() + .append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T').appendPattern("HH:mm:ss") + .appendPattern(".SSS").parseDefaulting(ChronoField.HOUR_OF_DAY, 0) + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0).parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) + .parseDefaulting(ChronoField.NANO_OF_SECOND, 0).appendOffset("+HH:MM", "Z").toFormatter() .withZone(GraphStoreConfiguration.DEFAULT_TIME_ZONE); - DATE_PRINTER = ISODateTimeFormat.date().withZone(GraphStoreConfiguration.DEFAULT_TIME_ZONE); - DATE_TIME_PRINTER = ISODateTimeFormat.dateTime().withZone(GraphStoreConfiguration.DEFAULT_TIME_ZONE); DATE_PRINTERS_BY_TIMEZONE = new HashMap<>(); DATE_TIME_PRINTERS_BY_TIMEZONE = new HashMap<>(); @@ -275,30 +296,30 @@ private AttributeUtils() { // Only static methods } - private static DateTimeFormatter getDateTimeFormatterByTimeZone(Map cache, DateTimeFormatter baseFormatter, DateTimeZone timeZone) { - if (timeZone == null) { + private static DateTimeFormatter getDateTimeFormatterByTimeZone(Map cache, DateTimeFormatter baseFormatter, ZonedDateTime zonedDateTime) { + if (zonedDateTime == null) { return baseFormatter; } - DateTimeFormatter formatter = cache.get(timeZone); + DateTimeFormatter formatter = cache.get(zonedDateTime.getZone()); if (formatter == null) { - formatter = baseFormatter.withZone(timeZone); - cache.put(timeZone, formatter); + formatter = baseFormatter.withZone(zonedDateTime.getZone()); + cache.put(zonedDateTime.getZone(), formatter); } return formatter; } - private static DateTimeFormatter getDateTimeParserByTimeZone(DateTimeZone timeZone) { - return getDateTimeFormatterByTimeZone(DATE_TIME_PARSERS_BY_TIMEZONE, DATE_TIME_PARSER, timeZone); + private static DateTimeFormatter getDateTimeParserByTimeZone(ZonedDateTime zonedDateTime) { + return getDateTimeFormatterByTimeZone(DATE_TIME_PARSERS_BY_TIMEZONE, DATE_TIME_PARSER, zonedDateTime); } - private static DateTimeFormatter getDateTimePrinterByTimeZone(DateTimeZone timeZone) { - return getDateTimeFormatterByTimeZone(DATE_TIME_PRINTERS_BY_TIMEZONE, DATE_TIME_PRINTER, timeZone); + private static DateTimeFormatter getDateTimePrinterByTimeZone(ZonedDateTime zonedDateTime) { + return getDateTimeFormatterByTimeZone(DATE_TIME_PRINTERS_BY_TIMEZONE, DATE_TIME_PRINTER, zonedDateTime); } - private static DateTimeFormatter getDatePrinterByTimeZone(DateTimeZone timeZone) { - return getDateTimeFormatterByTimeZone(DATE_PRINTERS_BY_TIMEZONE, DATE_PRINTER, timeZone); + private static DateTimeFormatter getDatePrinterByTimeZone(ZonedDateTime zonedDateTime) { + return getDateTimeFormatterByTimeZone(DATE_PRINTERS_BY_TIMEZONE, DATE_PRINTER, zonedDateTime); } /** @@ -316,18 +337,18 @@ public static String print(Object value) { * * @param value value * @param timeFormat time format - * @param timeZone time zone + * @param zonedDateTime zoned date time * @return string representation */ - public static String print(Object value, TimeFormat timeFormat, DateTimeZone timeZone) { + public static String print(Object value, TimeFormat timeFormat, ZonedDateTime zonedDateTime) { if (value == null) { return "null"; } if (value instanceof TimeSet) { - return ((TimeSet) value).toString(timeFormat, timeZone); + return ((TimeSet) value).toString(timeFormat, zonedDateTime); } if (value instanceof TimeMap) { - return ((TimeMap) value).toString(timeFormat, timeZone); + return ((TimeMap) value).toString(timeFormat, zonedDateTime); } if (value.getClass().isArray()) { return printArray(value); @@ -341,12 +362,12 @@ public static String print(Object value, TimeFormat timeFormat, DateTimeZone tim * * @param str string to parse * @param typeClass class of the desired type - * @param timeZone time zone to use or null to use default time zone (UTC), for - * dynamic types only + * @param zonedDateTime time zone to use or null to use default time zone (UTC), + * for dynamic types only * @return an instance of the type class, or null if str is null or * empty */ - public static Object parse(String str, Class typeClass, DateTimeZone timeZone) { + public static Object parse(String str, Class typeClass, ZonedDateTime zonedDateTime) { if (str == null || str.isEmpty()) { return null; } @@ -397,48 +418,48 @@ public static Object parse(String str, Class typeClass, DateTimeZone timeZone) { // Interval types: if (typeClass.equals(IntervalSet.class)) { - return IntervalsParser.parseIntervalSet(str, timeZone); + return IntervalsParser.parseIntervalSet(str, zonedDateTime); } else if (typeClass.equals(IntervalStringMap.class)) { - return IntervalsParser.parseIntervalMap(String.class, str, timeZone); + return IntervalsParser.parseIntervalMap(String.class, str, zonedDateTime); } else if (typeClass.equals(IntervalByteMap.class)) { - return IntervalsParser.parseIntervalMap(Byte.class, str, timeZone); + return IntervalsParser.parseIntervalMap(Byte.class, str, zonedDateTime); } else if (typeClass.equals(IntervalShortMap.class)) { - return IntervalsParser.parseIntervalMap(Short.class, str, timeZone); + return IntervalsParser.parseIntervalMap(Short.class, str, zonedDateTime); } else if (typeClass.equals(IntervalIntegerMap.class)) { - return IntervalsParser.parseIntervalMap(Integer.class, str, timeZone); + return IntervalsParser.parseIntervalMap(Integer.class, str, zonedDateTime); } else if (typeClass.equals(IntervalLongMap.class)) { - return IntervalsParser.parseIntervalMap(Long.class, str, timeZone); + return IntervalsParser.parseIntervalMap(Long.class, str, zonedDateTime); } else if (typeClass.equals(IntervalFloatMap.class)) { - return IntervalsParser.parseIntervalMap(Float.class, str, timeZone); + return IntervalsParser.parseIntervalMap(Float.class, str, zonedDateTime); } else if (typeClass.equals(IntervalDoubleMap.class)) { - return IntervalsParser.parseIntervalMap(Double.class, str, timeZone); + return IntervalsParser.parseIntervalMap(Double.class, str, zonedDateTime); } else if (typeClass.equals(IntervalBooleanMap.class)) { - return IntervalsParser.parseIntervalMap(Boolean.class, str, timeZone); + return IntervalsParser.parseIntervalMap(Boolean.class, str, zonedDateTime); } else if (typeClass.equals(IntervalCharMap.class)) { - return IntervalsParser.parseIntervalMap(Character.class, str, timeZone); + return IntervalsParser.parseIntervalMap(Character.class, str, zonedDateTime); } // Timestamp types: if (typeClass.equals(TimestampSet.class)) { - return TimestampsParser.parseTimestampSet(str, timeZone); + return TimestampsParser.parseTimestampSet(str, zonedDateTime); } else if (typeClass.equals(TimestampStringMap.class)) { - return TimestampsParser.parseTimestampMap(String.class, str, timeZone); + return TimestampsParser.parseTimestampMap(String.class, str, zonedDateTime); } else if (typeClass.equals(TimestampByteMap.class)) { - return TimestampsParser.parseTimestampMap(Byte.class, str, timeZone); + return TimestampsParser.parseTimestampMap(Byte.class, str, zonedDateTime); } else if (typeClass.equals(TimestampShortMap.class)) { - return TimestampsParser.parseTimestampMap(Short.class, str, timeZone); + return TimestampsParser.parseTimestampMap(Short.class, str, zonedDateTime); } else if (typeClass.equals(TimestampIntegerMap.class)) { - return TimestampsParser.parseTimestampMap(Integer.class, str, timeZone); + return TimestampsParser.parseTimestampMap(Integer.class, str, zonedDateTime); } else if (typeClass.equals(TimestampLongMap.class)) { - return TimestampsParser.parseTimestampMap(Long.class, str, timeZone); + return TimestampsParser.parseTimestampMap(Long.class, str, zonedDateTime); } else if (typeClass.equals(TimestampFloatMap.class)) { - return TimestampsParser.parseTimestampMap(Float.class, str, timeZone); + return TimestampsParser.parseTimestampMap(Float.class, str, zonedDateTime); } else if (typeClass.equals(TimestampDoubleMap.class)) { - return TimestampsParser.parseTimestampMap(Double.class, str, timeZone); + return TimestampsParser.parseTimestampMap(Double.class, str, zonedDateTime); } else if (typeClass.equals(TimestampBooleanMap.class)) { - return TimestampsParser.parseTimestampMap(Boolean.class, str, timeZone); + return TimestampsParser.parseTimestampMap(Boolean.class, str, zonedDateTime); } else if (typeClass.equals(TimestampCharMap.class)) { - return TimestampsParser.parseTimestampMap(Character.class, str, timeZone); + return TimestampsParser.parseTimestampMap(Character.class, str, zonedDateTime); } // Array types: @@ -1015,11 +1036,14 @@ public static String getTypeName(Class type) { * Parses the given time and returns its milliseconds representation. * * @param dateTime type to parse - * @param timeZone time zone to use or null to use default time zone (UTC) + * @param zonedDateTime time zone to use or null to use default time zone (UTC) * @return milliseconds representation + * @throws DateTimeParseException if the time cannot be parsed */ - public static double parseDateTime(String dateTime, DateTimeZone timeZone) { - return getDateTimeParserByTimeZone(timeZone).parseDateTime(dateTime).getMillis(); + public static double parseDateTime(String dateTime, ZonedDateTime zonedDateTime) throws DateTimeParseException { + DateTimeFormatter dateTimeParserByTimeZone = getDateTimeParserByTimeZone(zonedDateTime); + Instant instant = dateTimeParserByTimeZone.parse(dateTime, Instant::from); + return (double) instant.toEpochMilli(); } /** @@ -1028,8 +1052,9 @@ public static double parseDateTime(String dateTime, DateTimeZone timeZone) { * * @param dateTime the type to parse * @return milliseconds representation + * @throws DateTimeParseException if the time cannot be parsed */ - public static double parseDateTime(String dateTime) { + public static double parseDateTime(String dateTime) throws DateTimeParseException { return parseDateTime(dateTime, null); } @@ -1038,11 +1063,12 @@ public static double parseDateTime(String dateTime) { * Returns the date or timestamp converted to a timestamp in milliseconds. * * @param timeStr Date or timestamp string - * @param timeZone Time zone to use or null to use default time zone (UTC) + * @param zonedDateTime Time zone to use or null to use default time zone (UTC) * @return Timestamp + * @throws DateTimeParseException if the time cannot be parsed */ - public static double parseDateTimeOrTimestamp(String timeStr, DateTimeZone timeZone) { - return FormattingAndParsingUtils.parseDateTimeOrTimestamp(timeStr, timeZone); + public static double parseDateTimeOrTimestamp(String timeStr, ZonedDateTime zonedDateTime) throws DateTimeParseException { + return FormattingAndParsingUtils.parseDateTimeOrTimestamp(timeStr, zonedDateTime); } /** @@ -1052,9 +1078,10 @@ public static double parseDateTimeOrTimestamp(String timeStr, DateTimeZone timeZ * * @param timeStr Date or timestamp string * @return Timestamp + * @throws DateTimeParseException if the time cannot be parsed */ - public static double parseDateTimeOrTimestamp(String timeStr) { - return FormattingAndParsingUtils.parseDateTimeOrTimestamp(timeStr, null); + public static double parseDateTimeOrTimestamp(String timeStr) throws DateTimeParseException { + return FormattingAndParsingUtils.parseDateTimeOrTimestamp(timeStr); } /** @@ -1071,14 +1098,17 @@ public static String printTimestamp(double timestamp) { * Returns the date's string representation of the given timestamp. * * @param timestamp time, in milliseconds - * @param timeZone time zone to use or null to use default time zone (UTC) + * @param zonedDatetime time zone to use or null to use default time zone (UTC) * @return formatted date */ - public static String printDate(double timestamp, DateTimeZone timeZone) { + public static String printDate(double timestamp, ZonedDateTime zonedDatetime) { if (Double.isInfinite(timestamp) || Double.isNaN(timestamp)) { return printTimestamp(timestamp); } - return getDatePrinterByTimeZone(timeZone).print((long) timestamp); + Instant ofEpochMilli = Instant.ofEpochMilli((long) timestamp); + DateTimeFormatter datePrinterByTimeZone = getDatePrinterByTimeZone(zonedDatetime); + ZonedDateTime zonedDateTime = ofEpochMilli.atZone(datePrinterByTimeZone.getZone()); + return zonedDateTime.format(datePrinterByTimeZone); } /** @@ -1096,14 +1126,18 @@ public static String printDate(double timestamp) { * Returns the time's string representation of the given timestamp. * * @param timestamp time, in milliseconds - * @param timeZone time zone to use or null to use default time zone (UTC) + * @param zonedDateTime time zone to use or null to use default time zone (UTC) * @return formatted time */ - public static String printDateTime(double timestamp, DateTimeZone timeZone) { + public static String printDateTime(double timestamp, ZonedDateTime zonedDateTime) { if (Double.isInfinite(timestamp) || Double.isNaN(timestamp)) { return printTimestamp(timestamp); } - return getDateTimePrinterByTimeZone(timeZone).print((long) timestamp); + DateTimeFormatter dateTimePrinterByTimeZone = getDateTimePrinterByTimeZone(zonedDateTime); + Instant ofEpochMilli = Instant.ofEpochMilli((long) timestamp); + ZonedDateTime zonedDateTime2 = ofEpochMilli.atZone(dateTimePrinterByTimeZone.getZone()); + OffsetDateTime time = OffsetDateTime.from(zonedDateTime2); + return time.format(dateTimePrinterByTimeZone); } /** @@ -1122,15 +1156,15 @@ public static String printDateTime(double timestamp) { * * @param timestamp time, in milliseconds * @param timeFormat time format - * @param timeZone time zone to use or null to use default time zone (UTC). + * @param zonedDateTime time zone to use or null to use default time zone (UTC). * @return formatted timestamp */ - public static String printTimestampInFormat(double timestamp, TimeFormat timeFormat, DateTimeZone timeZone) { + public static String printTimestampInFormat(double timestamp, TimeFormat timeFormat, ZonedDateTime zonedDateTime) { switch (timeFormat) { case DATE: - return AttributeUtils.printDate(timestamp, timeZone); + return AttributeUtils.printDate(timestamp, zonedDateTime); case DATETIME: - return AttributeUtils.printDateTime(timestamp, timeZone); + return AttributeUtils.printDateTime(timestamp, zonedDateTime); case DOUBLE: return AttributeUtils.printTimestamp(timestamp); } diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index a748a566..7f4b1498 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -18,8 +18,8 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.time.ZoneId; import org.gephi.graph.impl.GraphModelImpl; -import org.joda.time.DateTimeZone; /** * Graph API's entry point. @@ -700,14 +700,14 @@ public static interface DefaultColumns { * * @return time zone */ - public DateTimeZone getTimeZone(); + public ZoneId getTimeZone(); /** * Sets the time zone used to display time. * * @param timeZone time zone */ - public void setTimeZone(DateTimeZone timeZone); + public void setTimeZone(ZoneId timeZone); /** * Returns the current configuration. diff --git a/src/main/java/org/gephi/graph/api/types/IntervalMap.java b/src/main/java/org/gephi/graph/api/types/IntervalMap.java index 67c917af..8be74214 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalMap.java @@ -19,12 +19,12 @@ import org.gephi.graph.api.Estimator; import java.math.BigDecimal; import java.math.RoundingMode; +import java.time.ZonedDateTime; import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.impl.FormattingAndParsingUtils; -import org.joda.time.DateTimeZone; /** * Abstract class that implement a sorted map between intervals and attribute @@ -604,7 +604,7 @@ protected Double getAverageDouble(final Interval interval) { } @Override - public String toString(TimeFormat timeFormat, DateTimeZone timeZone) { + public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { if (size == 0) { return FormattingAndParsingUtils.EMPTY_VALUE; } diff --git a/src/main/java/org/gephi/graph/api/types/IntervalSet.java b/src/main/java/org/gephi/graph/api/types/IntervalSet.java index d886869a..70ac6285 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalSet.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalSet.java @@ -15,12 +15,12 @@ */ package org.gephi.graph.api.types; +import java.time.ZonedDateTime; import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.impl.FormattingAndParsingUtils; -import org.joda.time.DateTimeZone; /** * Sorted set for intervals. @@ -351,7 +351,7 @@ public boolean equals(Object obj) { } @Override - public String toString(TimeFormat timeFormat, DateTimeZone timeZone) { + public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { if (size == 0) { return FormattingAndParsingUtils.EMPTY_VALUE; } diff --git a/src/main/java/org/gephi/graph/api/types/TimeMap.java b/src/main/java/org/gephi/graph/api/types/TimeMap.java index eecf07f4..d25db48a 100644 --- a/src/main/java/org/gephi/graph/api/types/TimeMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimeMap.java @@ -15,10 +15,10 @@ */ package org.gephi.graph.api.types; +import java.time.ZonedDateTime; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; -import org.joda.time.DateTimeZone; /** * Interface that defines the functionalities both timestamp and interval map @@ -140,5 +140,5 @@ public interface TimeMap { * @param timeZone time zone * @return map as string */ - public String toString(TimeFormat timeFormat, DateTimeZone timeZone); + public String toString(TimeFormat timeFormat, ZonedDateTime timeZone); } diff --git a/src/main/java/org/gephi/graph/api/types/TimeSet.java b/src/main/java/org/gephi/graph/api/types/TimeSet.java index f1653751..e09f9f08 100644 --- a/src/main/java/org/gephi/graph/api/types/TimeSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimeSet.java @@ -15,8 +15,8 @@ */ package org.gephi.graph.api.types; +import java.time.ZonedDateTime; import org.gephi.graph.api.TimeFormat; -import org.joda.time.DateTimeZone; /** * Interface that defines the functionalities both timestamp and interval set @@ -130,5 +130,5 @@ public interface TimeSet { * @param timeZone time zone * @return set as string */ - public String toString(TimeFormat timeFormat, DateTimeZone timeZone); + public String toString(TimeFormat timeFormat, ZonedDateTime timeZone); } diff --git a/src/main/java/org/gephi/graph/api/types/TimestampMap.java b/src/main/java/org/gephi/graph/api/types/TimestampMap.java index 21b3792b..1e6b02a7 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampMap.java @@ -19,12 +19,12 @@ import org.gephi.graph.api.Estimator; import java.math.BigDecimal; import java.math.RoundingMode; +import java.time.ZonedDateTime; import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.impl.FormattingAndParsingUtils; -import org.joda.time.DateTimeZone; /** * Abstract class that implement a sorted map between timestamp and attribute @@ -455,7 +455,7 @@ protected Double getAverageDouble(final Interval interval) { } @Override - public String toString(TimeFormat timeFormat, DateTimeZone timeZone) { + public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { if (size == 0) { return FormattingAndParsingUtils.EMPTY_VALUE; } diff --git a/src/main/java/org/gephi/graph/api/types/TimestampSet.java b/src/main/java/org/gephi/graph/api/types/TimestampSet.java index a0e36092..2eb485e1 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampSet.java @@ -15,11 +15,11 @@ */ package org.gephi.graph.api.types; +import java.time.ZonedDateTime; import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.impl.FormattingAndParsingUtils; -import org.joda.time.DateTimeZone; /** * Sorted set for timestamps. @@ -217,7 +217,7 @@ public boolean equals(Object obj) { } @Override - public String toString(TimeFormat timeFormat, DateTimeZone timeZone) { + public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { if (size == 0) { return FormattingAndParsingUtils.EMPTY_VALUE; } diff --git a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java index bea1d2a5..bd14e00b 100644 --- a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java +++ b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java @@ -20,8 +20,9 @@ import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; import org.gephi.graph.api.AttributeUtils; -import org.joda.time.DateTimeZone; /** * Utils for formatting and parsing special data types (dynamic intervals, @@ -50,8 +51,9 @@ public final class FormattingAndParsingUtils { * @param timeStr Date or timestamp string * @param timeZone Time zone to use or null to use default time zone (UTC) * @return Timestamp + * @throws DateTimeParseException if the time cannot be parsed */ - public static double parseDateTimeOrTimestamp(String timeStr, DateTimeZone timeZone) { + public static double parseDateTimeOrTimestamp(String timeStr, ZonedDateTime timeZone) throws DateTimeParseException { double value; try { // Try first to parse as a single double: @@ -73,8 +75,9 @@ public static double parseDateTimeOrTimestamp(String timeStr, DateTimeZone timeZ * * @param timeStr Date or timestamp string * @return Timestamp + * @throws DateTimeParseException if the time cannot be parsed */ - public static double parseDateTimeOrTimestamp(String timeStr) { + public static double parseDateTimeOrTimestamp(String timeStr) throws DateTimeParseException { return parseDateTimeOrTimestamp(timeStr, null); } diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 2e53c09a..143ef225 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -15,6 +15,7 @@ */ package org.gephi.graph.impl; +import java.time.ZoneId; import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Configuration; @@ -39,7 +40,6 @@ import org.gephi.graph.api.UndirectedGraph; import org.gephi.graph.api.UndirectedSubgraph; import org.gephi.graph.api.TimeIndex; -import org.joda.time.DateTimeZone; import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalDoubleMap; import org.gephi.graph.api.types.IntervalSet; @@ -404,12 +404,12 @@ public void setTimeFormat(TimeFormat timeFormat) { } @Override - public DateTimeZone getTimeZone() { + public ZoneId getTimeZone() { return store.timeZone; } @Override - public void setTimeZone(DateTimeZone timeZone) { + public void setTimeZone(ZoneId timeZone) { store.timeZone = timeZone; } diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 77386112..aa57984b 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -17,6 +17,7 @@ package org.gephi.graph.impl; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -43,7 +44,6 @@ import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.TimestampSet; -import org.joda.time.DateTimeZone; public class GraphStore implements DirectedGraph, DirectedSubgraph { @@ -72,7 +72,7 @@ public class GraphStore implements DirectedGraph, DirectedSubgraph { // TimeFormat protected TimeFormat timeFormat; // Time zone - protected DateTimeZone timeZone; + protected ZoneId timeZone; // Spatial context protected SpatialIndexImpl spatialIndex; // Default columns diff --git a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java index 897dd625..6c8eba6c 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java +++ b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java @@ -15,10 +15,11 @@ */ package org.gephi.graph.impl; +import java.time.ZoneId; +import java.time.ZonedDateTime; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.api.TimeRepresentation; -import org.joda.time.DateTimeZone; public final class GraphStoreConfiguration { @@ -76,7 +77,7 @@ public final class GraphStoreConfiguration { // TimeFormat public static final TimeFormat DEFAULT_TIME_FORMAT = TimeFormat.DOUBLE; // Time zone - public static final DateTimeZone DEFAULT_TIME_ZONE = DateTimeZone.UTC; + public static final ZoneId DEFAULT_TIME_ZONE = ZoneId.of("UTC"); // Dynamics public static final Estimator DEFAULT_ESTIMATOR = Estimator.FIRST; public static final TimeRepresentation DEFAULT_TIME_REPRESENTATION = TimeRepresentation.TIMESTAMP; diff --git a/src/main/java/org/gephi/graph/impl/IntervalsParser.java b/src/main/java/org/gephi/graph/impl/IntervalsParser.java index 9de3b4ae..0be6fcfe 100644 --- a/src/main/java/org/gephi/graph/impl/IntervalsParser.java +++ b/src/main/java/org/gephi/graph/impl/IntervalsParser.java @@ -17,6 +17,8 @@ import java.io.IOException; import java.io.StringReader; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.List; import org.gephi.graph.api.AttributeUtils; @@ -37,7 +39,6 @@ import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.IntervalShortMap; import org.gephi.graph.api.types.IntervalStringMap; -import org.joda.time.DateTimeZone; import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; /** @@ -100,7 +101,7 @@ public final class IntervalsParser { * input string or bounds cannot be parsed into doubles or * dates/datetimes. */ - public static IntervalSet parseIntervalSet(String input, DateTimeZone timeZone) throws IllegalArgumentException { + public static IntervalSet parseIntervalSet(String input, ZonedDateTime timeZone) throws IllegalArgumentException { if (input == null) { return null; } @@ -155,7 +156,7 @@ public static IntervalSet parseIntervalSet(String input) throws IllegalArgumentE * are no intervals in the input string or bounds cannot be parsed into * doubles or dates/datetimes. */ - public static IntervalMap parseIntervalMap(Class typeClass, String input, DateTimeZone timeZone) throws IllegalArgumentException { + public static IntervalMap parseIntervalMap(Class typeClass, String input, ZonedDateTime timeZone) throws IllegalArgumentException { if (typeClass == null) { throw new IllegalArgumentException("typeClass required"); } @@ -237,7 +238,7 @@ public static IntervalMap parseIntervalMap(Class typeClass, String inp * @param timeZone Time zone to use or null to use default time zone (UTC) * @return List of Interval */ - private static List> parseIntervals(Class typeClass, String input, DateTimeZone timeZone) throws IOException, IllegalArgumentException { + private static List> parseIntervals(Class typeClass, String input, ZonedDateTime timeZone) throws IOException, IllegalArgumentException { if (input == null) { return null; } @@ -279,7 +280,7 @@ private static List> parseIntervals(Class typeClass, return intervals; } - private static IntervalWithValue parseInterval(Class typeClass, StringReader reader, DateTimeZone timeZone) throws IOException { + private static IntervalWithValue parseInterval(Class typeClass, StringReader reader, ZonedDateTime timeZone) throws IOException { ArrayList values = new ArrayList<>(); int r; @@ -312,23 +313,27 @@ private static IntervalWithValue parseInterval(Class typeClass, String return buildInterval(typeClass, values, timeZone); } - private static IntervalWithValue buildInterval(Class typeClass, ArrayList values, DateTimeZone timeZone) { + private static IntervalWithValue buildInterval(Class typeClass, ArrayList values, ZonedDateTime timeZone) { if (typeClass == null && values.size() != 2) { throw new IllegalArgumentException("Each interval must have 2 values"); } else if (typeClass != null && values.size() != 3) { throw new IllegalArgumentException("Each interval must have 3 values"); } - double low = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(0), timeZone); - double high = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(1), timeZone); + try { + double low = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(0), timeZone); + double high = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(1), timeZone); - if (typeClass == null) { - return new IntervalWithValue(low, high, null); - } else { - String valString = values.get(2); - Object value = FormattingAndParsingUtils.convertValue(typeClass, valString); + if (typeClass == null) { + return new IntervalWithValue(low, high, null); + } else { + String valString = values.get(2); + Object value = FormattingAndParsingUtils.convertValue(typeClass, valString); - return new IntervalWithValue(low, high, value); + return new IntervalWithValue(low, high, value); + } + } catch (DateTimeParseException ex) { + throw new IllegalArgumentException("Invalid date/time/timestamp value", ex); } } diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index f70e4fe5..d80b8134 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -52,6 +52,7 @@ import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.ZoneId; import java.util.Date; import java.util.List; import java.util.Locale; @@ -92,7 +93,6 @@ import org.gephi.graph.impl.NodeImpl.NodePropertiesImpl; import org.gephi.graph.impl.utils.DataInputOutput; import org.gephi.graph.impl.utils.LongPacker; -import org.joda.time.DateTimeZone; // Greatly inspired from JDBM https://github.com/jankotek/JDBM3 public class Serialization { @@ -1041,14 +1041,14 @@ private TimeFormat deserializeTimeFormat(final DataInput is) throws IOException, return tf; } - private void serializeTimeZone(final DataOutput out, final DateTimeZone timeZone) throws IOException { - serialize(out, timeZone.getID()); + private void serializeTimeZone(final DataOutput out, final ZoneId timeZone) throws IOException { + serialize(out, timeZone.getId()); } - private DateTimeZone deserializeTimeZone(final DataInput is) throws IOException, ClassNotFoundException { + private ZoneId deserializeTimeZone(final DataInput is) throws IOException, ClassNotFoundException { String id = (String) deserialize(is); - DateTimeZone tz = DateTimeZone.forID(id); + ZoneId tz = ZoneId.of(id); model.store.timeZone = tz; return tz; @@ -1576,8 +1576,8 @@ protected void serialize(final DataOutput out, final Object obj) throws IOExcept TimeFormat b = (TimeFormat) obj; out.write(TIME_FORMAT); serializeTimeFormat(out, b); - } else if (obj instanceof DateTimeZone) { - DateTimeZone b = (DateTimeZone) obj; + } else if (obj instanceof ZoneId) { + ZoneId b = (ZoneId) obj; out.write(TIME_ZONE); serializeTimeZone(out, b); } else if (obj instanceof TimeStore) { diff --git a/src/main/java/org/gephi/graph/impl/TimestampsParser.java b/src/main/java/org/gephi/graph/impl/TimestampsParser.java index 6d075194..c5b57db2 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampsParser.java +++ b/src/main/java/org/gephi/graph/impl/TimestampsParser.java @@ -17,6 +17,8 @@ import java.io.IOException; import java.io.StringReader; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import org.gephi.graph.api.AttributeUtils; import static org.gephi.graph.impl.FormattingAndParsingUtils.COMMA; @@ -37,7 +39,6 @@ import org.gephi.graph.api.types.TimestampSet; import org.gephi.graph.api.types.TimestampShortMap; import org.gephi.graph.api.types.TimestampStringMap; -import org.joda.time.DateTimeZone; import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; /** @@ -95,7 +96,7 @@ public final class TimestampsParser { * input string or bounds cannot be parsed into doubles or * dates/datetimes. */ - public static TimestampSet parseTimestampSet(String input, DateTimeZone timeZone) throws IllegalArgumentException { + public static TimestampSet parseTimestampSet(String input, ZonedDateTime timeZone) throws IllegalArgumentException { if (input == null) { return null; } @@ -151,8 +152,12 @@ public static TimestampSet parseTimestampSet(String input, DateTimeZone timeZone TimestampSet result = new TimestampSet(values.size()); - for (String value : values) { - result.add(FormattingAndParsingUtils.parseDateTimeOrTimestamp(value, timeZone)); + try { + for (String value : values) { + result.add(FormattingAndParsingUtils.parseDateTimeOrTimestamp(value, timeZone)); + } + } catch (DateTimeParseException ex) { + throw new IllegalArgumentException("Invalid timestamp value: " + ex.getMessage(), ex); } return result; @@ -189,7 +194,7 @@ public static TimestampSet parseTimestampSet(String input) throws IllegalArgumen * are no timestamps in the input string or bounds cannot be parsed into * doubles or dates/datetimes. */ - public static TimestampMap parseTimestampMap(Class typeClass, String input, DateTimeZone timeZone) throws IllegalArgumentException { + public static TimestampMap parseTimestampMap(Class typeClass, String input, ZonedDateTime timeZone) throws IllegalArgumentException { if (typeClass == null) { throw new IllegalArgumentException("typeClass required"); } @@ -275,7 +280,7 @@ public static TimestampMap parseTimestampMap(Class typeClass, String i return parseTimestampMap(typeClass, input, null); } - private static void parseTimestampAndValue(Class typeClass, StringReader reader, TimestampMap result, DateTimeZone timeZone) throws IOException { + private static void parseTimestampAndValue(Class typeClass, StringReader reader, TimestampMap result, ZonedDateTime timeZone) throws IOException { ArrayList values = new ArrayList<>(); int r; @@ -309,16 +314,20 @@ private static void parseTimestampAndValue(Class typeClass, StringReader addTimestampAndValue(typeClass, values, result, timeZone); } - private static void addTimestampAndValue(Class typeClass, ArrayList values, TimestampMap result, DateTimeZone timeZone) { + private static void addTimestampAndValue(Class typeClass, ArrayList values, TimestampMap result, ZonedDateTime timeZone) { if (values.size() != 2) { throw new IllegalArgumentException("Each timestamp and value array must have 2 values"); } - double timestamp = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(0), timeZone); + try { + double timestamp = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(0), timeZone); - String valString = values.get(1); - T value = FormattingAndParsingUtils.convertValue(typeClass, valString); + String valString = values.get(1); + T value = FormattingAndParsingUtils.convertValue(typeClass, valString); - result.put(timestamp, value); + result.put(timestamp, value); + } catch (DateTimeParseException ex) { + throw new IllegalArgumentException("Invalid timestamp value: " + values.get(0), ex); + } } } diff --git a/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java b/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java index f1a2bf8c..79ba798e 100644 --- a/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java +++ b/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java @@ -18,11 +18,12 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; +import java.time.ZoneId; +import java.time.ZonedDateTime; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; -import org.joda.time.DateTimeZone; import org.testng.Assert; import org.testng.annotations.Test; @@ -534,12 +535,12 @@ public void testToStringDate() { .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0, foo]; [1342483341000.0, 1342483380000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1 - .toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone - .forID("+03:00")), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone - .forID("-03:00")), "<[2012-02-28, 2012-02-29, foo]; [2012-07-16, 2012-07-16, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("UTC"))), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("+03:00"))), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("-03:00"))), "<[2012-02-28, 2012-02-29, foo]; [2012-07-16, 2012-07-16, bar]>"); // Test infinity: IntervalStringMap mapInf = new IntervalStringMap(); @@ -566,10 +567,10 @@ public void testToStringDatetime() { .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0, foo]; [1342487444000.0, 1342487445000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1 - .toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, DateTimeZone - .forID("+12:30")), "<[2012-02-29T12:30:00.000+12:30, 2012-03-01T12:30:00.000+12:30, foo]; [2012-07-17T13:40:44.000+12:30, 2012-07-17T13:40:45.000+12:30, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId + .of("UTC"))), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId + .of("+12:30"))), "<[2012-02-29T12:30:00.000+12:30, 2012-03-01T12:30:00.000+12:30, foo]; [2012-07-17T13:40:44.000+12:30, 2012-07-17T13:40:45.000+12:30, bar]>"); // Test with timezone parsing and UTC printing: IntervalStringMap map2 = new IntervalStringMap(); diff --git a/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java b/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java index 9c0f080b..106b3a95 100644 --- a/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java +++ b/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java @@ -15,10 +15,11 @@ */ package org.gephi.graph.api.types; +import java.time.ZoneId; +import java.time.ZonedDateTime; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; -import org.joda.time.DateTimeZone; import org.testng.Assert; import org.testng.annotations.Test; @@ -366,16 +367,16 @@ public void testToStringDate() { .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0]; [1342483341000.0, 1342483380000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1 - .toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone - .forID("+12:00")), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("UTC"))), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("+12:00"))), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); set1.add(new Interval(AttributeUtils.parseDateTime("2012-07-18T18:30:00"), AttributeUtils.parseDateTime("2012-07-18T18:30:01"))); - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone - .forID("+08:00")), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]; [2012-07-19, 2012-07-19]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone - .forID("-10:00")), "<[2012-02-28, 2012-02-29]; [2012-07-16, 2012-07-16]; [2012-07-18, 2012-07-18]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime.now(ZoneId + .of("+08:00"))), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]; [2012-07-19, 2012-07-19]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime.now(ZoneId + .of("-10:00"))), "<[2012-02-28, 2012-02-29]; [2012-07-16, 2012-07-16]; [2012-07-18, 2012-07-18]>"); // Test infinity: IntervalSet setInf = new IntervalSet(); @@ -401,10 +402,10 @@ public void testToStringDatetime() { .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0]; [1342487444000.0, 1342487445000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1 - .toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, DateTimeZone - .forID("+12:00")), "<[2012-02-29T12:00:00.000+12:00, 2012-03-01T12:00:00.000+12:00]; [2012-07-17T13:10:44.000+12:00, 2012-07-17T13:10:45.000+12:00]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId + .of("UTC"))), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId + .of("+12:00"))), "<[2012-02-29T12:00:00.000+12:00, 2012-03-01T12:00:00.000+12:00]; [2012-07-17T13:10:44.000+12:00, 2012-07-17T13:10:45.000+12:00]>"); // Test with timezone parsing and UTC printing: IntervalSet set2 = new IntervalSet(); diff --git a/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java b/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java index 4cb0fcd4..f22e8573 100644 --- a/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java +++ b/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java @@ -17,11 +17,12 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.time.ZoneId; +import java.time.ZonedDateTime; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; -import org.joda.time.DateTimeZone; import org.testng.Assert; import org.testng.annotations.Test; @@ -642,11 +643,12 @@ public void testToStringDate() { Assert.assertEquals(map1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, foo]; [1330473741000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1.toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, foo]; [2012-02-29, bar]>"); - Assert.assertEquals(map1 - .toString(TimeFormat.DATE, DateTimeZone.forID("+03:00")), "<[2012-02-29, foo]; [2012-02-29, bar]>"); - Assert.assertEquals(map1 - .toString(TimeFormat.DATE, DateTimeZone.forID("-03:00")), "<[2012-02-28, foo]; [2012-02-28, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("UTC"))), "<[2012-02-29, foo]; [2012-02-29, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("+03:00"))), "<[2012-02-29, foo]; [2012-02-29, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("-03:00"))), "<[2012-02-28, foo]; [2012-02-28, bar]>"); // Test infinity: TimestampStringMap mapInf = new TimestampStringMap(); @@ -670,10 +672,10 @@ public void testToStringDatetime() { Assert.assertEquals(map1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, foo]; [1330477844000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1 - .toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, DateTimeZone - .forID("-01:30")), "<[2012-02-28T22:30:00.000-01:30, foo]; [2012-02-28T23:40:44.000-01:30, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZonedDateTime + .now(ZoneId.of("UTC"))), "<[2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId + .of("-01:30"))), "<[2012-02-28T22:30:00.000-01:30, foo]; [2012-02-28T23:40:44.000-01:30, bar]>"); // Test with timezone parsing and UTC printing: TimestampStringMap map2 = new TimestampStringMap(); @@ -769,7 +771,7 @@ private void testValues(TimestampMap set, double[] expectedTimestamp, Object[] e Assert.assertEquals(set.get(expectedTimestamp[i], null), expectedValues[i]); Assert.assertEquals(set.get(999999.0, getDefaultValue(set)), getDefaultValue(set)); Assert.assertTrue(set.contains(expectedTimestamp[i])); - Assert.assertEquals(keysArray[i], expectedTimestamp[i]); + Assert.assertEquals(keysArray[i], expectedTimestamp[i], 0.0); if (typeClass != String.class) { try { diff --git a/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java b/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java index bcf2729a..c195205f 100644 --- a/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java +++ b/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java @@ -17,12 +17,13 @@ import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet; import it.unimi.dsi.fastutil.doubles.DoubleSet; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Random; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.impl.NumberGenerator; -import org.joda.time.DateTimeZone; import org.testng.Assert; import org.testng.annotations.Test; @@ -323,13 +324,15 @@ public void testToStringDate() { Assert.assertEquals(set1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330473741000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.UTC), "<[2012-02-29, 2012-02-29]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATE, DateTimeZone.forID("+12:00")), "<[2012-02-29, 2012-02-29]>"); - set1.add(AttributeUtils.parseDateTime("2012-07-18T18:30:00")); Assert.assertEquals(set1 - .toString(TimeFormat.DATE, DateTimeZone.forID("+08:00")), "<[2012-02-29, 2012-02-29, 2012-07-19]>"); + .toString(TimeFormat.DATE, ZonedDateTime.now(ZoneId.of("UTC"))), "<[2012-02-29, 2012-02-29]>"); Assert.assertEquals(set1 - .toString(TimeFormat.DATE, DateTimeZone.forID("-10:00")), "<[2012-02-28, 2012-02-28, 2012-07-18]>"); + .toString(TimeFormat.DATE, ZonedDateTime.now(ZoneId.of("+12:00"))), "<[2012-02-29, 2012-02-29]>"); + set1.add(AttributeUtils.parseDateTime("2012-07-18T18:30:00")); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("+08:00"))), "<[2012-02-29, 2012-02-29, 2012-07-19]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime + .now(ZoneId.of("-10:00"))), "<[2012-02-28, 2012-02-28, 2012-07-18]>"); // Test infinity: TimestampSet setInf = new TimestampSet(); @@ -353,10 +356,10 @@ public void testToStringDatetime() { Assert.assertEquals(set1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330477844000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1 - .toString(TimeFormat.DATETIME, DateTimeZone.UTC), "<[2012-02-29T00:00:00.000Z, 2012-02-29T01:10:44.000Z]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, DateTimeZone - .forID("+12:15")), "<[2012-02-29T12:15:00.000+12:15, 2012-02-29T13:25:44.000+12:15]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZonedDateTime + .now(ZoneId.of("UTC"))), "<[2012-02-29T00:00:00.000Z, 2012-02-29T01:10:44.000Z]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZonedDateTime + .now(ZoneId.of("+12:15"))), "<[2012-02-29T12:15:00.000+12:15, 2012-02-29T13:25:44.000+12:15]>"); // Test with timezone parsing and UTC printing: TimestampSet set2 = new TimestampSet(); diff --git a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java index 63612b5c..646f06a4 100644 --- a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java +++ b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java @@ -18,6 +18,8 @@ import java.awt.Color; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -55,7 +57,6 @@ import org.gephi.graph.api.types.IntervalShortMap; import org.gephi.graph.api.types.IntervalStringMap; import org.gephi.graph.api.types.TimestampSet; -import org.joda.time.DateTimeZone; import org.testng.Assert; import org.testng.annotations.Test; @@ -248,9 +249,9 @@ public void testParseDynamicTimestampTypesWithTimeZone() { Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00]>", TimestampSet.class), AttributeUtils .parse("<[2015-01-01T00:00:00]>", TimestampSet.class, null)); Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00]>", TimestampSet.class), AttributeUtils - .parse("<[2015-01-01T00:00:00]>", TimestampSet.class, DateTimeZone.UTC)); + .parse("<[2015-01-01T00:00:00]>", TimestampSet.class, ZonedDateTime.now(ZoneId.of("UTC")))); Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00]>", TimestampSet.class), AttributeUtils - .parse("<[2015-01-01T01:30:00]>", TimestampSet.class, DateTimeZone.forID("+01:30"))); + .parse("<[2015-01-01T01:30:00]>", TimestampSet.class, ZonedDateTime.now(ZoneId.of("+01:30")))); // Maps Assert.assertEquals(AttributeUtils @@ -258,10 +259,12 @@ public void testParseDynamicTimestampTypesWithTimeZone() { .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, null)); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, DateTimeZone.UTC)); + .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, ZonedDateTime + .now(ZoneId.of("UTC")))); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils - .parse("<[2015-01-01T01:30:00, val]>", TimestampStringMap.class, DateTimeZone.forID("+01:30"))); + .parse("<[2015-01-01T01:30:00, val]>", TimestampStringMap.class, ZonedDateTime + .now(ZoneId.of("+01:30")))); } @Test @@ -272,11 +275,12 @@ public void testParseDynamicIntervalTypesWithTimeZone() { .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, null)); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, DateTimeZone.UTC)); + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, ZonedDateTime + .now(ZoneId.of("UTC")))); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils - .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00]>", IntervalSet.class, DateTimeZone - .forID("-02:00"))); + .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00]>", IntervalSet.class, ZonedDateTime + .now(ZoneId.of("-02:00")))); // Maps Assert.assertEquals(AttributeUtils @@ -284,11 +288,12 @@ public void testParseDynamicIntervalTypesWithTimeZone() { .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, null)); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, DateTimeZone.UTC)); + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, ZonedDateTime + .now(ZoneId.of("UTC")))); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class), AttributeUtils - .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00, val]>", IntervalStringMap.class, DateTimeZone - .forID("-02:00"))); + .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00, val]>", IntervalStringMap.class, ZonedDateTime + .now(ZoneId.of("-02:00")))); } @Test(expectedExceptions = IllegalArgumentException.class) @@ -572,8 +577,10 @@ public void testStandardizeValueMixedMapKeyContent() { public void testParseDate() { Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00", null), 0.0); - Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00", DateTimeZone.UTC), 0.0); - Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T01:30:00", DateTimeZone.forID("+01:30")), 0.0); + Assert.assertEquals(AttributeUtils + .parseDateTime("1970-01-01T00:00:00", ZonedDateTime.now(ZoneId.of("UTC"))), 0.0); + Assert.assertEquals(AttributeUtils + .parseDateTime("1970-01-01T01:30:00", ZonedDateTime.now(ZoneId.of("+01:30"))), 0.0); Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00+00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00Z"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00.000+00:00"), 0.0); @@ -585,9 +592,9 @@ public void testParseDate() { AttributeUtils.parseDateTime("20040401"); Assert.assertEquals(AttributeUtils.parseDateTime("2012-09-12T15:04:01"), AttributeUtils - .parseDateTime("2012-09-12T15:04:01", DateTimeZone.forID("+00:00"))); + .parseDateTime("2012-09-12T15:04:01", ZonedDateTime.now(ZoneId.of("+00:00")))); Assert.assertEquals(AttributeUtils.parseDateTime("2012-09-12T15:04:01+03:30"), AttributeUtils - .parseDateTime("2012-09-12T15:04:01", DateTimeZone.forID("+03:30"))); + .parseDateTime("2012-09-12T15:04:01", ZonedDateTime.now(ZoneId.of("+03:30")))); } @Test @@ -605,14 +612,15 @@ public void testParseDateTimeOrTimestamp() { Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("0"), AttributeUtils .parseDateTimeOrTimestamp("1970-01-01T00:00:00Z")); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("0"), AttributeUtils - .parseDateTimeOrTimestamp("1970-01-01T00:00:00", DateTimeZone.forID("+00:00"))); + .parseDateTimeOrTimestamp("1970-01-01T00:00:00", ZonedDateTime.now(ZoneId.of("+00:00")))); // Dates Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00", null), 0.0); - Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00", DateTimeZone.UTC), 0.0); Assert.assertEquals(AttributeUtils - .parseDateTimeOrTimestamp("1970-01-01T01:30:00", DateTimeZone.forID("+01:30")), 0.0); + .parseDateTimeOrTimestamp("1970-01-01T00:00:00", ZonedDateTime.now(ZoneId.of("UTC"))), 0.0); + Assert.assertEquals(AttributeUtils + .parseDateTimeOrTimestamp("1970-01-01T01:30:00", ZonedDateTime.now(ZoneId.of("+01:30"))), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00+00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00Z"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00.000+00:00"), 0.0); @@ -620,9 +628,9 @@ public void testParseDateTimeOrTimestamp() { Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1969-12-31T22:00:00-02:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("2012-09-12T15:04:01"), AttributeUtils - .parseDateTime("2012-09-12T15:04:01", DateTimeZone.forID("+00:00"))); + .parseDateTime("2012-09-12T15:04:01", ZonedDateTime.now(ZoneId.of("+00:00")))); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("2012-09-12T15:04:01+03:30"), AttributeUtils - .parseDateTime("2012-09-12T15:04:01", DateTimeZone.forID("+03:30"))); + .parseDateTime("2012-09-12T15:04:01", ZonedDateTime.now(ZoneId.of("+03:30")))); } @Test @@ -632,16 +640,16 @@ public void testPrintDate() { Assert.assertEquals(AttributeUtils.printDate(d), date); - Assert.assertEquals(AttributeUtils.printDate(d, DateTimeZone.UTC), date); + Assert.assertEquals(AttributeUtils.printDate(d, ZonedDateTime.now(ZoneId.of("UTC"))), date); Assert.assertEquals(AttributeUtils.printDate(d, null), date); - Assert.assertEquals(AttributeUtils.printDate(d, DateTimeZone.forID("+00:30")), "2003-01-01");// Still - // same - // day - Assert.assertEquals(AttributeUtils.printDate(d, DateTimeZone.forID("+12:00")), "2003-01-01");// Still - // same - // day - Assert.assertEquals(AttributeUtils.printDate(d, DateTimeZone.forID("-00:30")), "2002-12-31");// Previous - // day + Assert.assertEquals(AttributeUtils.printDate(d, ZonedDateTime.now(ZoneId.of("+00:30"))), "2003-01-01");// Still + // same + // day + Assert.assertEquals(AttributeUtils.printDate(d, ZonedDateTime.now(ZoneId.of("+12:00"))), "2003-01-01");// Still + // same + // day + Assert.assertEquals(AttributeUtils.printDate(d, ZonedDateTime.now(ZoneId.of("-00:30"))), "2002-12-31");// Previous + // day } @Test @@ -652,19 +660,19 @@ public void testPrintDateTime() { String dateInUTC = AttributeUtils.printDateTime(d); Assert.assertEquals(AttributeUtils.parseDateTime(dateInUTC), d); - Assert.assertEquals(AttributeUtils.printDateTime(d, DateTimeZone.UTC), dateInUTC); + Assert.assertEquals(AttributeUtils.printDateTime(d, ZonedDateTime.now(ZoneId.of("UTC"))), dateInUTC); Assert.assertEquals(AttributeUtils.printDateTime(d, null), dateInUTC); Assert.assertEquals(AttributeUtils.printDateTime(d), "2003-01-01T08:00:00.000Z"); Assert.assertEquals(AttributeUtils - .printDateTime(d, DateTimeZone.forID("+00:30")), "2003-01-01T08:30:00.000+00:30"); + .printDateTime(d, ZonedDateTime.now(ZoneId.of("+00:30"))), "2003-01-01T08:30:00.000+00:30"); Assert.assertEquals(AttributeUtils - .printDateTime(d, DateTimeZone.forID("+12:00")), "2003-01-01T20:00:00.000+12:00"); + .printDateTime(d, ZonedDateTime.now(ZoneId.of("+12:00"))), "2003-01-01T20:00:00.000+12:00"); Assert.assertEquals(AttributeUtils - .printDateTime(d, DateTimeZone.forID("-12:00")), "2002-12-31T20:00:00.000-12:00"); + .printDateTime(d, ZonedDateTime.now(ZoneId.of("-12:00"))), "2002-12-31T20:00:00.000-12:00"); Assert.assertEquals(AttributeUtils.printDateTime(AttributeUtils - .parseDateTime("2003-01-01T16:00:00", DateTimeZone.forID("+00:00")), DateTimeZone - .forID("+12:00")), "2003-01-02T04:00:00.000+12:00"); + .parseDateTime("2003-01-01T16:00:00", ZonedDateTime.now(ZoneId.of("+00:00"))), ZonedDateTime + .now(ZoneId.of("+12:00"))), "2003-01-02T04:00:00.000+12:00"); } @Test @@ -693,16 +701,16 @@ public void testPrint() { Assert.assertEquals(AttributeUtils.print(ts), ts.toString(TimeFormat.DOUBLE)); Assert.assertEquals(AttributeUtils.print(ts, TimeFormat.DATE, null), ts.toString(TimeFormat.DATE, null)); - Assert.assertEquals(AttributeUtils.print(ts, TimeFormat.DATETIME, DateTimeZone.forID("+00:30")), ts - .toString(TimeFormat.DATETIME, DateTimeZone.forID("+00:30"))); + Assert.assertEquals(AttributeUtils.print(ts, TimeFormat.DATETIME, ZonedDateTime.now(ZoneId.of("+00:30"))), ts + .toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId.of("+00:30")))); TimestampIntegerMap tm = new TimestampIntegerMap(); tm.put(d, 42); Assert.assertEquals(AttributeUtils.print(tm), tm.toString(TimeFormat.DOUBLE)); Assert.assertEquals(AttributeUtils.print(tm, TimeFormat.DATE, null), tm.toString(TimeFormat.DATE, null)); - Assert.assertEquals(AttributeUtils.print(tm, TimeFormat.DATETIME, DateTimeZone.forID("+00:30")), tm - .toString(TimeFormat.DATETIME, DateTimeZone.forID("+00:30"))); + Assert.assertEquals(AttributeUtils.print(tm, TimeFormat.DATETIME, ZonedDateTime.now(ZoneId.of("+00:30"))), tm + .toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId.of("+00:30")))); } @Test diff --git a/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/src/test/java/org/gephi/graph/impl/GraphModelTest.java index 75af99f7..7eb3c45d 100644 --- a/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -16,6 +16,7 @@ package org.gephi.graph.impl; import java.io.IOException; +import java.time.ZoneId; import java.util.Arrays; import org.gephi.graph.api.Column; import org.gephi.graph.api.Configuration; @@ -34,7 +35,6 @@ import org.testng.Assert; import org.testng.annotations.Test; import org.gephi.graph.api.TimeIndex; -import org.joda.time.DateTimeZone; import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalDoubleMap; import org.gephi.graph.api.types.IntervalSet; @@ -249,11 +249,11 @@ public void testSetTimeFormat() { @Test public void testSetTimeZone() { GraphModelImpl graphModel = new GraphModelImpl(); - Assert.assertEquals(graphModel.getTimeZone(), DateTimeZone.UTC);// Default - graphModel.setTimeZone(DateTimeZone.forID("-02:00")); - Assert.assertEquals(graphModel.getTimeZone(), DateTimeZone.forID("-02:00")); - graphModel.setTimeZone(DateTimeZone.UTC); - Assert.assertEquals(graphModel.getTimeZone(), DateTimeZone.UTC); + Assert.assertEquals(graphModel.getTimeZone(), ZoneId.of("UTC"));// Default + graphModel.setTimeZone(ZoneId.of("-02:00")); + Assert.assertEquals(graphModel.getTimeZone(), ZoneId.of("-02:00")); + graphModel.setTimeZone(ZoneId.of("UTC")); + Assert.assertEquals(graphModel.getTimeZone(), ZoneId.of("UTC")); } @Test diff --git a/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java b/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java index 145dedf2..cef3d1ad 100644 --- a/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java +++ b/src/test/java/org/gephi/graph/impl/IntervalsParserTest.java @@ -17,6 +17,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.format.DateTimeParseException; import java.util.Date; import java.util.TimeZone; import org.gephi.graph.api.Interval; @@ -100,8 +101,6 @@ public void testParseIntervalSet() throws ParseException { // Dates: assertEquals(buildIntervalSet(new Interval(parseDateIntoTimestamp("2015-01-01"), parseDateIntoTimestamp("2015-01-31"))), IntervalsParser.parseIntervalSet("[2015-01-01, 2015-01-31]")); - assertEquals(buildIntervalSet(new Interval(parseDateIntoTimestamp("2015-01-01"), - parseDateIntoTimestamp("2015-01-31"))), IntervalsParser.parseIntervalSet("[2015-01, 2015-01-31]")); // Date times: assertEquals(buildIntervalSet(new Interval(parseDateTimeIntoTimestamp("2015-01-01 21:12:05"), diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 9cd89e87..9a07b019 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -45,6 +45,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -83,7 +84,6 @@ import org.gephi.graph.api.types.IntervalShortMap; import org.gephi.graph.api.types.IntervalStringMap; import org.gephi.graph.impl.utils.DataInputOutput; -import org.joda.time.DateTimeZone; import org.testng.Assert; import org.testng.annotations.Test; @@ -681,12 +681,12 @@ public void testTimeFormat() throws IOException, ClassNotFoundException { public void testTimeZone() throws IOException, ClassNotFoundException { GraphModelImpl graphModel = new GraphModelImpl(); GraphStore store = graphModel.store; - store.timeZone = DateTimeZone.forID("+01:30"); + store.timeZone = ZoneId.of("+01:30"); Serialization ser = new Serialization(graphModel); byte[] buf = ser.serialize(store.timeZone); - DateTimeZone l = (DateTimeZone) ser.deserialize(buf); - Assert.assertEquals(DateTimeZone.forID("+01:30"), l); + ZoneId l = (ZoneId) ser.deserialize(buf); + Assert.assertEquals(ZoneId.of("+01:30"), l); } @Test diff --git a/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java b/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java index 674d7507..20bf5009 100644 --- a/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java +++ b/src/test/java/org/gephi/graph/impl/TimestampsParserTest.java @@ -17,6 +17,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.format.DateTimeParseException; import java.util.Date; import java.util.TimeZone; import org.gephi.graph.api.types.TimestampBooleanMap; @@ -96,8 +97,6 @@ public void testParseTimestampSet() throws ParseException { // Dates: assertEquals(buildTimestampSet(parseDateIntoTimestamp("2015-01-01"), parseDateIntoTimestamp("2015-01-31")), TimestampsParser .parseTimestampSet("[2015-01-01, 2015-01-31]")); - assertEquals(buildTimestampSet(parseDateIntoTimestamp("2015-01-01"), parseDateIntoTimestamp("2015-01-31")), TimestampsParser - .parseTimestampSet("[2015-01, 2015-01-31]")); // Date times: assertEquals(buildTimestampSet(parseDateTimeIntoTimestamp("2015-01-01 21:12:05"), parseDateTimeIntoTimestamp("2015-01-02 00:00:00")), TimestampsParser From 54255f7ae85fc5407a27b692111d0f5f223876b7 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 29 Apr 2023 21:31:01 +0200 Subject: [PATCH 121/271] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c99bebd7..f0b0ba0e 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ Development builds can be found on [Sonatype's Snapshot Repository](https://oss. API Documentation is available [here](https://www.javadoc.io/doc/org.gephi/graphstore/latest/index.html). +Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) to get started. + ## Dependencies GraphStore depends on FastUtil >= 6.0 and Colt 1.2.0. @@ -42,6 +44,8 @@ For a complete list of dependencies, consult the `pom.xml` file. GraphStore uses Maven for building. > mvn clean install + +Note that code formatting is automatically applied at that time. ### How to test From 9b812e2ef9291519edbf6a27ae43737e24c8f65f Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 29 Apr 2023 21:49:01 +0200 Subject: [PATCH 122/271] Set Java11 as minimum version. (#169) Co-authored-by: Julien Gouesse --- pom.xml | 39 +++---------------- .../org/gephi/graph/api/AttributeUtils.java | 12 +++--- 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/pom.xml b/pom.xml index abb2e93e..76d13a0d 100644 --- a/pom.xml +++ b/pom.xml @@ -50,8 +50,8 @@ UTF-8 UTF-8 - 1.8 - 1.8 + 11 + 11 github @@ -65,7 +65,7 @@ it.unimi.dsi fastutil - 8.5.11 + 8.5.12 colt @@ -80,12 +80,12 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.11.0 org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M7 + 3.0.0 org.apache.maven.plugins @@ -95,7 +95,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.4.1 + 3.5.0 org.apache.maven.plugins @@ -125,11 +125,6 @@ - - org.codehaus.mojo - animal-sniffer-maven-plugin - 1.18 - net.revelc.code.formatter formatter-maven-plugin @@ -211,28 +206,6 @@ org.eluder.coveralls coveralls-maven-plugin - - - - org.codehaus.mojo - animal-sniffer-maven-plugin - - - org.codehaus.mojo.signature - java18 - 1.0 - - - - - check-java-api - test - - check - - - - diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index 365c6544..a54ef952 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -385,17 +385,17 @@ public static Object parse(String str, Class typeClass, ZonedDateTime zonedDateT if (typeClass.equals(String.class)) { return str; } else if (typeClass.equals(Byte.class)) { - return new Byte(str); + return Byte.valueOf(str); } else if (typeClass.equals(Short.class)) { - return new Short(str); + return Short.valueOf(str); } else if (typeClass.equals(Integer.class)) { - return new Integer(str); + return Integer.valueOf(str); } else if (typeClass.equals(Long.class)) { - return new Long(str); + return Long.valueOf(str); } else if (typeClass.equals(Float.class)) { - return new Float(str); + return Float.valueOf(str); } else if (typeClass.equals(Double.class)) { - return new Double(str); + return Double.valueOf(str); } else if (typeClass.equals(BigInteger.class)) { return new BigInteger(str); } else if (typeClass.equals(BigDecimal.class)) { From 99ef2a35b2e0dec8bdc5327c853cc47b8209308b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 29 Apr 2023 21:49:57 +0200 Subject: [PATCH 123/271] #162 Configuration refactoring (#168) * Add config builder * Work in progress * Fix tests * Serialization * Stop using deprecated config * Edge type autoregistration disabled * Additional tests for non default configs * Add missing config methods * Documentation --- src/main/java/org/gephi/graph/api/Column.java | 2 + .../org/gephi/graph/api/Configuration.java | 596 +++++++++++++++--- .../java/org/gephi/graph/api/GraphModel.java | 23 +- .../java/org/gephi/graph/impl/ColumnImpl.java | 8 +- .../gephi/graph/impl/ColumnNoIndexImpl.java | 2 +- .../graph/impl/ColumnStandardIndexImpl.java | 3 +- .../org/gephi/graph/impl/ColumnStore.java | 44 +- .../gephi/graph/impl/ConfigurationImpl.java | 235 +++++++ .../gephi/graph/impl/DefaultColumnsImpl.java | 24 +- .../java/org/gephi/graph/impl/EdgeImpl.java | 7 +- .../java/org/gephi/graph/impl/EdgeStore.java | 13 +- .../org/gephi/graph/impl/EdgeTypeStore.java | 11 +- .../org/gephi/graph/impl/GraphBridgeImpl.java | 73 ++- .../gephi/graph/impl/GraphFactoryImpl.java | 11 +- .../org/gephi/graph/impl/GraphModelImpl.java | 110 +--- .../java/org/gephi/graph/impl/GraphStore.java | 67 +- .../graph/impl/GraphStoreConfiguration.java | 22 +- .../java/org/gephi/graph/impl/NodeImpl.java | 3 +- .../org/gephi/graph/impl/Serialization.java | 103 +-- .../java/org/gephi/graph/impl/TableImpl.java | 30 +- .../java/org/gephi/graph/impl/TimeStore.java | 18 +- .../gephi/graph/impl/AttributeUtilsTest.java | 8 +- .../gephi/graph/impl/ColumnNoIndexTest.java | 10 +- .../gephi/graph/impl/ColumnObserverTest.java | 13 +- .../gephi/graph/impl/ConfigurationTest.java | 160 ++++- .../org/gephi/graph/impl/EdgeImplTest.java | 106 ++-- .../org/gephi/graph/impl/EdgeStoreTest.java | 35 +- .../gephi/graph/impl/EdgeTypeStoreTest.java | 5 +- .../org/gephi/graph/impl/ElementImplTest.java | 13 +- .../org/gephi/graph/impl/GraphBridgeTest.java | 26 +- .../gephi/graph/impl/GraphFactoryTest.java | 24 +- .../org/gephi/graph/impl/GraphGenerator.java | 18 +- .../org/gephi/graph/impl/GraphModelTest.java | 172 ++--- .../org/gephi/graph/impl/GraphStoreTest.java | 12 +- .../org/gephi/graph/impl/IndexImplTest.java | 33 +- .../graph/impl/IntervalIndexImplTest.java | 24 +- .../graph/impl/IntervalIndexStoreTest.java | 36 +- .../org/gephi/graph/impl/NodeImplTest.java | 26 + .../gephi/graph/impl/SerializationTest.java | 23 +- .../graph/impl/SpatialIndexImplTest.java | 28 +- .../org/gephi/graph/impl/TableImplTest.java | 70 +- .../gephi/graph/impl/TableObserverTest.java | 20 +- 42 files changed, 1506 insertions(+), 761 deletions(-) create mode 100644 src/main/java/org/gephi/graph/impl/ConfigurationImpl.java create mode 100644 src/test/java/org/gephi/graph/impl/NodeImplTest.java diff --git a/src/main/java/org/gephi/graph/api/Column.java b/src/main/java/org/gephi/graph/api/Column.java index c7d9b9c1..e7928127 100644 --- a/src/main/java/org/gephi/graph/api/Column.java +++ b/src/main/java/org/gephi/graph/api/Column.java @@ -152,6 +152,8 @@ public interface Column { * * @param withDiff true if column observer should provide column differences * @return the column observer + * @throws UnsupportedOperationException if observers are disabled (from + * Configuration) */ public ColumnObserver createColumnObserver(boolean withDiff); } diff --git a/src/main/java/org/gephi/graph/api/Configuration.java b/src/main/java/org/gephi/graph/api/Configuration.java index adca5dbb..adafe2b0 100644 --- a/src/main/java/org/gephi/graph/api/Configuration.java +++ b/src/main/java/org/gephi/graph/api/Configuration.java @@ -17,7 +17,7 @@ import org.gephi.graph.api.types.IntervalDoubleMap; import org.gephi.graph.api.types.TimestampDoubleMap; -import org.gephi.graph.impl.GraphStoreConfiguration; +import org.gephi.graph.impl.ConfigurationImpl; /** * Global configuration set at initialization. @@ -26,33 +26,438 @@ * {@link GraphModel.Factory#newInstance(org.gephi.graph.api.Configuration)} to * create a GraphModel with custom configuration. *

+ * Create instances by using the builder: + *

+ * Configuration config = Configuration.builder().build();
+ * 
+ *

* Note that setting configurations after the GraphModel has been * created won't have any effect. *

* By default, both node and edge id types are String.class and the * time representation is TIMESTAMP. + *

+ * See the builder documentation for more information on default values. * * @see GraphModel + * @see Builder */ public class Configuration { - private Class nodeIdType; - private Class edgeIdType; - private Class edgeLabelType; - private Class edgeWeightType; - private TimeRepresentation timeRepresentation; - private Boolean edgeWeightColumn; + private ConfigurationImpl delegate; /** * Default constructor. + * + * @deprecated Use the builder() method instead. */ + @Deprecated public Configuration() { - nodeIdType = GraphStoreConfiguration.DEFAULT_NODE_ID_TYPE; - edgeIdType = GraphStoreConfiguration.DEFAULT_EDGE_ID_TYPE; - edgeLabelType = GraphStoreConfiguration.DEFAULT_EDGE_LABEL_TYPE; - edgeWeightType = GraphStoreConfiguration.DEFAULT_EDGE_WEIGHT_TYPE; - timeRepresentation = GraphStoreConfiguration.DEFAULT_TIME_REPRESENTATION; - edgeWeightColumn = true; + this.delegate = new ConfigurationImpl(); + } + + protected Configuration(ConfigurationImpl delegate) { + this.delegate = delegate; + } + + /** + * Creates a new builder. + * + * @return a new builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Configuration builder. + *

+ * + * Note that this class is not thread-safe. + */ + public static class Builder { + + private ConfigurationImpl configuration; + + private Builder() { + configuration = new ConfigurationImpl(); + } + + private Builder(ConfigurationImpl configuration) { + this.configuration = configuration; + } + + /** + * Builds the configuration. + * + * @return the configuration + */ + public Configuration build() { + // Check for potential inconsistencies + if (!configuration.isEnableNodeProperties() && configuration.isEnableSpatialIndex()) { + throw new IllegalStateException("Spatial index can't be enabled if node properties are disabled"); + } + + return new Configuration(configuration); + } + + /** + * Sets the node id type. + *

+ * Only simple types such as primitives, wrappers and String are supported. + *

+ * Default is String.class. + * + * @param nodeIdType node id type + * @return this builder + * @throws IllegalArgumentException if the type isn't supported + */ + public Builder nodeIdType(final Class nodeIdType) { + checkSimpleType(nodeIdType); + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public Class getNodeIdType() { + return nodeIdType; + } + }); + return this; + } + + /** + * Sets the edge id type. + *

+ * Only simple types such as primitives, wrappers and String are supported. + *

+ * Default is String.class. + * + * @param edgeIdType edge id type + * @return this builder + * @throws IllegalArgumentException if the type isn't supported + */ + public Builder edgeIdType(final Class edgeIdType) { + checkSimpleType(edgeIdType); + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public Class getEdgeIdType() { + return edgeIdType; + } + }); + return this; + } + + /** + * Sets the edge label type. + *

+ * Only simple types such as primitives, wrappers and String are supported. + *

+ * Default is String.class. + * + * @param edgeLabelType edge label type + * @return this builder + * @throws IllegalArgumentException if the type isn't supported + */ + public Builder edgeLabelType(final Class edgeLabelType) { + checkSimpleType(edgeLabelType); + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public Class getEdgeLabelType() { + return edgeLabelType; + } + }); + return this; + } + + /** + * Sets the edge weight type. + *

+ * Double, IntervalDoubleMap and + * TimestampDoubleMap are supported. + *

+ * Default is Double.class. + * + * @param edgeWeightType edge weight type + * @return this builder + * @throws IllegalArgumentException if the type isn't supported + */ + public Builder edgeWeightType(final Class edgeWeightType) { + if (!(Double.class.equals(edgeWeightType) || TimestampDoubleMap.class + .equals(edgeWeightType) || IntervalDoubleMap.class.equals(edgeWeightType))) { + throw new IllegalArgumentException("Unsupported type " + edgeWeightType + .getCanonicalName() + ", should be Double, IntervalDoubleMap or TimestampDoubleMap"); + } + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public Class getEdgeWeightType() { + return edgeWeightType; + } + }); + return this; + } + + /** + * Sets the time representation. + *

+ * Default is TIMESTAMP. + * + * @param timeRepresentation time representation + * @return this builder + */ + public Builder timeRepresentation(final TimeRepresentation timeRepresentation) { + if (timeRepresentation == null) { + throw new IllegalArgumentException("timeRepresentation cannot be null"); + } + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public TimeRepresentation getTimeRepresentation() { + return timeRepresentation; + } + }); + return this; + } + + /** + * Sets whether to create an edge weight column. + *

+ * Default is true. + * + * @param edgeWeightColumn edge weight column + * @return this builder + */ + public Builder edgeWeightColumn(final boolean edgeWeightColumn) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public Boolean getEdgeWeightColumn() { + return edgeWeightColumn; + } + }); + return this; + } + + /** + * Sets whether to enable observers on tables and columns. + *

+ * Default is true. + * + * @param enableObservers enable observers + * @return this builder + */ + public Builder enableObservers(final boolean enableObservers) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableObservers() { + return enableObservers; + } + }); + return this; + } + + /** + * Sets whether to enable auto edge type registration. + *

+ * If enabled, edge types are automatically registered when edges are added. If + * disabled, one needs to call {@link GraphModel#addEdgeType(Object)} explicitly + * for each type. + *

+ * Default is true. + * + * @param enableAutoEdgeTypeRegistration enable auto edge type registration + * @return this builder + */ + public Builder enableAutoEdgeTypeRegistration(final boolean enableAutoEdgeTypeRegistration) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableAutoEdgeTypeRegistration() { + return enableAutoEdgeTypeRegistration; + } + }); + return this; + } + + /** + * Sets whether to enable node properties. + *

+ * If enabled, {@link NodeProperties} are created for each node. If those + * properties aren't needed, disabling them can save memory. + *

+ * Default is true. + * + * @param enableNodeProperties enable node properties + * @return this builder + */ + public Builder enableNodeProperties(final boolean enableNodeProperties) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableNodeProperties() { + return enableNodeProperties; + } + }); + return this; + } + + /** + * Sets whether to enable edge properties. + *

+ * If enabled, {@link EdgeProperties} are created for each edge. If those + * properties aren't needed, disabling them can save memory. + *

+ * Default is true. + * + * @param enableEdgeProperties enable edge properties + * @return this builder + */ + public Builder enableEdgeProperties(final boolean enableEdgeProperties) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableEdgeProperties() { + return enableEdgeProperties; + } + }); + return this; + } + + /** + * Sets whether to enable the {@link SpatialIndex}. + *

+ * If enabled, the spatial index is updated while node positions are updated. If + * unused, disabling it is recommended as it adds some overhead. + *

+ * The spatial index can be retrieved from {@link GraphModel#getSpatialIndex()}. + *

+ * Default is false. + * + * @param enableSpatialIndex enable edge properties + * @return this builder + */ + public Builder enableSpatialIndex(final boolean enableSpatialIndex) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableSpatialIndex() { + return enableSpatialIndex; + } + }); + return this; + } + + /** + * Sets whether to enable the reverse indexing of node attributes. + *

+ * If enabled, the reverse index is updated while node attributes are updated. + * This powers {@link GraphModel#getNodeIndex()} but has a negative impact on + * memory usage (as any reverse index does). When disabled, the features of + * {@link Index} are still available but need to iterate over all nodes to + * return results. + *

+ * Default is true. + * + * @param enableIndexNodes enable node attribute indexing + * @return this builder + */ + public Builder enableIndexNodes(final boolean enableIndexNodes) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableIndexNodes() { + return enableIndexNodes; + } + }); + return this; + } + + /** + * Sets whether to enable the reverse indexing of edge attributes. + *

+ * If enabled, the reverse index is updated while node attributes are updated. + * This powers {@link GraphModel#getEdgeIndex()} but has a negative impact on + * memory usage (as any reverse index does). When disabled, the features of + * {@link Index} are still available but need to iterate over all nodes to + * return results. + *

+ * Default is true. + * + * @param enableIndexEdges enable edge attribute indexing + * @return this builder + */ + public Builder enableIndexEdges(final boolean enableIndexEdges) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableIndexEdges() { + return enableIndexEdges; + } + }); + return this; + } + + /** + * Sets whether to enable the reverse indexing of timestamps and intervals. + *

+ * If enabled, the reverse index is updated while element's time set is updated. + * This powers {@link GraphModel#getNodeTimeIndex()} and + * {@link GraphModel#getEdgeTimeIndex()} ()} but has a negative impact on memory + * usage (as any reverse index does). + *

+ * Default is true. + * + * @param enableIndexTime enable time indexing + * @return this builder + */ + public Builder enableIndexTime(final boolean enableIndexTime) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableIndexTime() { + return enableIndexTime; + } + }); + return this; + } + + /** + * Sets whether to enable multiple edges of the same type between two nodes. + *

+ * If disabled, only a single edge of a given type can exist between two nodes. + *

+ * Default is false. + * + * @param enableParallelEdgesSameType enable parallel edges of the same type + * @return this builder + */ + public Builder enableParallelEdgesSameType(final boolean enableParallelEdgesSameType) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableParallelEdgesSameType() { + return enableParallelEdgesSameType; + } + }); + return this; + } + + /** + * Sets whether to enable auto locking when using read/write APIs. + *

+ * If disabled, the client is responsible for handling multithreading themselves + * or calling methods such as {@link Graph#readLock()} or + * {@link Graph#writeLock()}. If enabled, each read methods (including + * iterators) handle locking. Similarly, each write method handle locking. + *

+ * Default is true. + * + * @param enableAutoLocking enable auto locking for read/write operations + * @see GraphLock + * @return this builder + */ + public Builder enableAutoLocking(final boolean enableAutoLocking) { + this.configuration = new ConfigurationImpl(new Configuration(this.configuration) { + @Override + public boolean isEnableAutoLocking() { + return enableAutoLocking; + } + }); + return this; + } + + private static void checkSimpleType(Class type) { + if (!AttributeUtils.isSimpleType(type)) { + throw new IllegalArgumentException("Unsupported type " + type.getCanonicalName()); + } + } } /** @@ -61,7 +466,7 @@ public Configuration() { * @return node id type */ public Class getNodeIdType() { - return nodeIdType; + return delegate.getNodeIdType(); } /** @@ -69,14 +474,14 @@ public Class getNodeIdType() { *

* Only simple types such as primitives, wrappers and String are supported. * + * @deprecated Use {@link #builder()} instead. + * * @param nodeIdType node id type * @throws IllegalArgumentException if the type isn't supported */ + @Deprecated public void setNodeIdType(Class nodeIdType) { - if (!AttributeUtils.isSimpleType(nodeIdType)) { - throw new IllegalArgumentException("Unsupported type " + nodeIdType.getClass().getCanonicalName()); - } - this.nodeIdType = nodeIdType; + this.delegate = new Builder(this.delegate).nodeIdType(nodeIdType).configuration; } /** @@ -85,7 +490,7 @@ public void setNodeIdType(Class nodeIdType) { * @return edge id type */ public Class getEdgeIdType() { - return edgeIdType; + return delegate.getEdgeIdType(); } /** @@ -93,14 +498,14 @@ public Class getEdgeIdType() { *

* Only simple types such as primitives, wrappers and String are supported. * + * @deprecated Use {@link #builder()} instead. + * * @param edgeIdType edge id type * @throws IllegalArgumentException if the type isn't supported */ + @Deprecated public void setEdgeIdType(Class edgeIdType) { - if (!AttributeUtils.isSimpleType(edgeIdType)) { - throw new IllegalArgumentException("Unsupported type " + edgeIdType.getClass().getCanonicalName()); - } - this.edgeIdType = edgeIdType; + this.delegate = new Builder(this.delegate).edgeIdType(edgeIdType).configuration; } /** @@ -109,20 +514,20 @@ public void setEdgeIdType(Class edgeIdType) { * @return edge label type */ public Class getEdgeLabelType() { - return edgeLabelType; + return delegate.getEdgeLabelType(); } /** * Sets the edge label type. * + * @deprecated Use {@link #builder()} instead. + * * @param edgeLabelType edge label type * @throws IllegalArgumentException if the type isn't supported */ + @Deprecated public void setEdgeLabelType(Class edgeLabelType) { - if (!AttributeUtils.isSimpleType(edgeLabelType)) { - throw new IllegalArgumentException("Unsupported type " + edgeLabelType.getClass().getCanonicalName()); - } - this.edgeLabelType = edgeLabelType; + this.delegate = new Builder(this.delegate).edgeLabelType(edgeLabelType).configuration; } /** @@ -131,22 +536,20 @@ public void setEdgeLabelType(Class edgeLabelType) { * @return edge weight type */ public Class getEdgeWeightType() { - return edgeWeightType; + return delegate.getEdgeWeightType(); } /** * Sets the edge weight type. * + * @deprecated Use {@link #builder()} instead. + * * @param edgeWeightType edge weight type * @throws IllegalArgumentException if the type isn't supported */ + @Deprecated public void setEdgeWeightType(Class edgeWeightType) { - if (Double.class.equals(edgeWeightType) || TimestampDoubleMap.class - .equals(edgeWeightType) || IntervalDoubleMap.class.equals(edgeWeightType)) { - this.edgeWeightType = edgeWeightType; - } else { - throw new IllegalArgumentException("Unsupported type " + edgeWeightType.getClass().getCanonicalName()); - } + this.delegate = new Builder(this.delegate).edgeWeightType(edgeWeightType).configuration; } /** @@ -155,19 +558,19 @@ public void setEdgeWeightType(Class edgeWeightType) { * @return time representation */ public TimeRepresentation getTimeRepresentation() { - return timeRepresentation; + return delegate.getTimeRepresentation(); } /** * Sets the time representation. * + * @deprecated Use {@link #builder()} instead. + * * @param timeRepresentation time representation */ + @Deprecated public void setTimeRepresentation(TimeRepresentation timeRepresentation) { - if (timeRepresentation == null) { - throw new IllegalArgumentException("timeRepresentation cannot be null"); - } - this.timeRepresentation = timeRepresentation; + this.delegate = new Builder(this.delegate).timeRepresentation(timeRepresentation).configuration; } /** @@ -176,16 +579,60 @@ public void setTimeRepresentation(TimeRepresentation timeRepresentation) { * @return edge weight column */ public Boolean getEdgeWeightColumn() { - return edgeWeightColumn; + return delegate.isEdgeWeightColumn(); } /** * Sets whether to create an edge weight column. + *

+ * + * @deprecated Use {@link #builder()} instead. * * @param edgeWeightColumn edge weight column */ + @Deprecated public void setEdgeWeightColumn(Boolean edgeWeightColumn) { - this.edgeWeightColumn = edgeWeightColumn; + this.delegate = new Builder(this.delegate).edgeWeightColumn(edgeWeightColumn).configuration; + } + + public boolean isEnableAutoLocking() { + return delegate.isEnableAutoLocking(); + } + + public boolean isEnableAutoEdgeTypeRegistration() { + return delegate.isEnableAutoEdgeTypeRegistration(); + } + + public boolean isEnableIndexNodes() { + return delegate.isEnableIndexNodes(); + } + + public boolean isEnableIndexEdges() { + return delegate.isEnableIndexEdges(); + } + + public boolean isEnableIndexTime() { + return delegate.isEnableIndexTime(); + } + + public boolean isEnableObservers() { + return delegate.isEnableObservers(); + } + + public boolean isEnableNodeProperties() { + return delegate.isEnableNodeProperties(); + } + + public boolean isEnableEdgeProperties() { + return delegate.isEnableEdgeProperties(); + } + + public boolean isEnableSpatialIndex() { + return delegate.isEnableSpatialIndex(); + } + + public boolean isEnableParallelEdgesSameType() { + return delegate.isEnableParallelEdgesSameType(); } /** @@ -194,64 +641,25 @@ public void setEdgeWeightColumn(Boolean edgeWeightColumn) { * @return a copy of this configuration */ public Configuration copy() { - Configuration copy = new Configuration(); - copy.nodeIdType = nodeIdType; - copy.edgeIdType = edgeIdType; - copy.edgeLabelType = edgeLabelType; - copy.edgeWeightType = edgeWeightType; - copy.timeRepresentation = timeRepresentation; - copy.edgeWeightColumn = edgeWeightColumn; - return copy; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 19 * hash + (this.nodeIdType != null ? this.nodeIdType.hashCode() : 0); - hash = 19 * hash + (this.edgeIdType != null ? this.edgeIdType.hashCode() : 0); - hash = 19 * hash + (this.edgeLabelType != null ? this.edgeLabelType.hashCode() : 0); - hash = 19 * hash + (this.edgeWeightType != null ? this.edgeWeightType.hashCode() : 0); - hash = 19 * hash + (this.timeRepresentation != null ? this.timeRepresentation.hashCode() : 0); - hash = 19 * hash + (this.edgeWeightColumn != null ? this.edgeWeightColumn.hashCode() : 0); - return hash; + return new Configuration(delegate); } @Override - public boolean equals(Object obj) { - if (this == obj) { + public boolean equals(Object o) { + if (this == o) { return true; } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Configuration other = (Configuration) obj; - if (this.nodeIdType != other.nodeIdType && (this.nodeIdType == null || !this.nodeIdType - .equals(other.nodeIdType))) { - return false; - } - if (this.edgeIdType != other.edgeIdType && (this.edgeIdType == null || !this.edgeIdType - .equals(other.edgeIdType))) { + if (!(o instanceof Configuration)) { return false; } - if (this.edgeLabelType != other.edgeLabelType && (this.edgeLabelType == null || !this.edgeLabelType - .equals(other.edgeLabelType))) { - return false; - } - if (this.edgeWeightType != other.edgeWeightType && (this.edgeWeightType == null || !this.edgeWeightType - .equals(other.edgeWeightType))) { - return false; - } - if (this.timeRepresentation != other.timeRepresentation && (this.timeRepresentation == null || !this.timeRepresentation - .equals(other.timeRepresentation))) { - return false; - } - if (this.edgeWeightColumn != other.edgeWeightColumn && (this.edgeWeightColumn == null || !this.edgeWeightColumn - .equals(other.edgeWeightColumn))) { - return false; - } - return true; + + Configuration that = (Configuration) o; + + return delegate.equals(that.delegate); + } + + @Override + public int hashCode() { + return delegate.hashCode(); } } diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index 7f4b1498..0e138a1c 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -42,10 +42,17 @@ *

  * GraphModel model = GraphModel.Factory.newInstance();
  * 
+ * + * A Configuration object can be passed to the factory: + * + *
+ * Configuration configuration = Configuration.builder().build();
+ * GraphModel model = GraphModel.Factory.newInstance(configuration);
+ * 
* * This API revolves around a set of simple concepts. A GraphModel * encapsulate all elements and metadata associated with a graph structure. In - * other words its a single graph but it also contains configuration, indices, + * other words it's a single graph, but it also contains configuration, indices, * views and other less important services such as observers. *

* Then, GraphModel gives access to the Graph @@ -83,6 +90,7 @@ * null label, which is internally represented as zero. * * @see Graph + * @see Configuration * @see Element * @see Table * @see Column @@ -139,7 +147,8 @@ public static GraphModel read(DataInput input) throws IOException { /** * Read the input into the given graph model. The provided graph - * model should be empty. + * model should be empty and the configurations should match between the + * provided model and the one being read. * * @param input data input to read from * @return the graphmodel passed as parameter @@ -718,12 +727,16 @@ public static interface DefaultColumns { /** * Sets a new configuration for this graph model. - *

- * Note that this method only works if the graph model is empty. + * + * @deprecated setting configuration after graph model creation is no longer + * supported. Best is to use the {@link Configuration#builder()} to + * create a new configuration and then use it at graph model + * creation from + * {@link GraphModel.Factory#newInstance(Configuration)}. * * @param configuration new configuration - * @throws IllegalStateException if the graph model isn't empty */ + @Deprecated public void setConfiguration(Configuration configuration); /** diff --git a/src/main/java/org/gephi/graph/impl/ColumnImpl.java b/src/main/java/org/gephi/graph/impl/ColumnImpl.java index 8513a277..71760206 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnImpl.java @@ -67,7 +67,7 @@ public ColumnImpl(TableImpl table, String id, Class typeClass, String title, Obj this.indexed = indexed; this.readOnly = readOnly; this.dynamic = TimeMap.class.isAssignableFrom(typeClass) || TimeSet.class.isAssignableFrom(typeClass); - this.observers = GraphStoreConfiguration.ENABLE_OBSERVERS ? new ArrayList<>() : null; + this.observers = table != null && table.configuration.isEnableObservers() ? new ArrayList<>() : null; this.estimator = this.dynamic ? Estimator.FIRST : null; } @@ -194,14 +194,14 @@ public ColumnObserverImpl createColumnObserver(boolean withDiff) { synchronized (observers) { observers.add(observer); } - return observer; + } else { + throw new UnsupportedOperationException("Observers are disabled. Enable them in Configuration"); } - return null; } protected void destroyColumnObserver(ColumnObserverImpl observer) { - if (observers != null) { + if (observers != null && !observers.isEmpty()) { synchronized (observers) { observers.remove(observer); } diff --git a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java index 7731e8f6..14ca8c44 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnNoIndexImpl.java @@ -47,7 +47,7 @@ protected ColumnNoIndexImpl(ColumnImpl column, Graph graph, Class elementClas this.column = column; this.elementClass = elementClass; this.graph = graph; - this.graphLock = graph.getLock(); + this.graphLock = graph != null ? graph.getLock() : null; } private Iterator getElementIterator() { diff --git a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java index 566489b7..f5cd397b 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java @@ -61,7 +61,8 @@ public abstract class ColumnStandardIndexImpl implements C protected ColumnStandardIndexImpl(ColumnImpl column) { this.column = column; this.nullSet = new ValueSet<>(null); - this.lock = GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? new TableLockImpl() : null; + this.lock = column.table != null && column.table.configuration.isEnableAutoLocking() ? new TableLockImpl() + : null; } protected static boolean isSupportedType(ColumnImpl col) { diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index 964510d0..0dd98967 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -42,8 +42,9 @@ public class ColumnStore implements ColumnIterable { protected final static int NULL_ID = -1; protected final static short NULL_SHORT = Short.MIN_VALUE; // Configuration + protected final ConfigurationImpl configuration; + // GraphStore protected final GraphStore graphStore; - protected final Configuration configuration; // Element protected final Class elementType; // Columns @@ -60,44 +61,28 @@ public class ColumnStore implements ColumnIterable { protected int length; public ColumnStore(Class elementType, boolean indexed) { - this(null, elementType, indexed); + this(null, elementType); } - public ColumnStore(GraphStore graphStore, Class elementType, boolean indexed) { + public ColumnStore(GraphStore graphStore, Class elementType) { if (MAX_SIZE >= Short.MAX_VALUE - Short.MIN_VALUE + 1) { throw new RuntimeException("Column Store size can't exceed 65534"); } this.graphStore = graphStore; - this.configuration = graphStore != null ? graphStore.configuration : new Configuration(); - this.lock = GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? new TableLockImpl() : null; + if (graphStore == null) { + // Used for testing only + configuration = new ConfigurationImpl(); + } else { + configuration = graphStore.configuration; + } + this.lock = configuration.isEnableAutoLocking() ? new TableLockImpl() : null; this.garbageQueue = new ShortRBTreeSet(); this.idMap = new Object2ShortOpenHashMap<>(MAX_SIZE); this.columns = new ColumnImpl[MAX_SIZE]; this.elementType = elementType; - this.indexStore = indexed ? new IndexStore<>(this) : null; + this.indexStore = new IndexStore<>(this); idMap.defaultReturnValue(NULL_SHORT); - this.observers = GraphStoreConfiguration.ENABLE_OBSERVERS ? new ArrayList<>() : null; - } - - private void updateConfiguration(Column changedColumn) { - String columnId = changedColumn.getId(); - if (Edge.class.equals(elementType)) { - if (columnId.equals(GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID)) { - if (hasColumn(columnId)) { - Class edgeWeightColumnClass = getColumn(columnId).getTypeClass(); - configuration.setEdgeWeightType(edgeWeightColumnClass); - configuration.setEdgeWeightColumn(true); - } else { - configuration.setEdgeWeightColumn(false); - } - } else if (columnId.equals(GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID)) { - configuration.setEdgeIdType(changedColumn.getTypeClass()); - } - } else if (Node.class.equals(elementType)) { - if (columnId.equals(GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID)) { - configuration.setNodeIdType(changedColumn.getTypeClass()); - } - } + this.observers = new ArrayList<>(); } public void addColumn(final Column column) { @@ -127,8 +112,6 @@ public void addColumn(final Column column) { indexStore.addColumn(columnImpl); } - updateConfiguration(column); - // Index attributes if (graphStore != null && columnImpl.table != null) { for (Element e : graphStore.getElements(columnImpl.table)) { @@ -169,7 +152,6 @@ public void removeColumn(final Column column) { indexStore.removeColumn((ColumnImpl) column); } columnImpl.setStoreId(NULL_ID); - updateConfiguration(column); } finally { unlock(); } diff --git a/src/main/java/org/gephi/graph/impl/ConfigurationImpl.java b/src/main/java/org/gephi/graph/impl/ConfigurationImpl.java new file mode 100644 index 00000000..85785194 --- /dev/null +++ b/src/main/java/org/gephi/graph/impl/ConfigurationImpl.java @@ -0,0 +1,235 @@ +package org.gephi.graph.impl; + +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.TimeRepresentation; + +public class ConfigurationImpl { + + // Node Id Type (default String) + private final Class nodeIdType; + // Edge Id Type (default String) + private final Class edgeIdType; + // Edge Label Type (default String) + private final Class edgeLabelType; + // Edge Weight Type (default Double) + private final Class edgeWeightType; + // Time representation (default Timestamp) + private final TimeRepresentation timeRepresentation; + // Use edge weight column, or just double (default True) + private final boolean edgeWeightColumn; + // Automatically use read/write locks when iterating/writing graph elements + // (default True) + private final boolean enableAutoLocking; + // Automatically register edge types when adding elements (default True) + private final boolean enableAutoEdgeTypeRegistration; + // Enable reverse index for node attributes (default True) + private final boolean enableIndexNodes; + // Enable reverse index for edge attributes (default True) + private final boolean enableIndexEdges; + // Enable reverse index for timestamps (default True) + private final boolean enableIndexTime; + // Enable observers (default True) + private final boolean enableObservers; + // Node properties are X, Y, Color etc. (default True) + private final boolean enableNodeProperties; + // Edge properties are Color, etc. (default True) + private final boolean enableEdgeProperties; + // Enable spatial index (default False) + private final boolean enableSpatialIndex; + // Enable parallel edges of the same type (default True) + private final boolean enableParallelEdgesSameType; + + public ConfigurationImpl() { + nodeIdType = GraphStoreConfiguration.DEFAULT_NODE_ID_TYPE; + edgeIdType = GraphStoreConfiguration.DEFAULT_EDGE_ID_TYPE; + edgeLabelType = GraphStoreConfiguration.DEFAULT_EDGE_LABEL_TYPE; + edgeWeightType = GraphStoreConfiguration.DEFAULT_EDGE_WEIGHT_TYPE; + timeRepresentation = GraphStoreConfiguration.DEFAULT_TIME_REPRESENTATION; + edgeWeightColumn = GraphStoreConfiguration.DEFAULT_ENABLE_EDGE_WEIGHT_COLUMN; + enableAutoLocking = GraphStoreConfiguration.DEFAULT_ENABLE_AUTO_LOCKING; + enableAutoEdgeTypeRegistration = GraphStoreConfiguration.DEFAULT_ENABLE_AUTO_EDGE_TYPE_REGISTRATION; + enableIndexNodes = GraphStoreConfiguration.DEFAULT_ENABLE_INDEX_NODES; + enableIndexEdges = GraphStoreConfiguration.DEFAULT_ENABLE_INDEX_EDGES; + enableIndexTime = GraphStoreConfiguration.DEFAULT_ENABLE_INDEX_TIME; + enableObservers = GraphStoreConfiguration.DEFAULT_ENABLE_OBSERVERS; + enableNodeProperties = GraphStoreConfiguration.DEFAULT_ENABLE_NODE_PROPERTIES; + enableEdgeProperties = GraphStoreConfiguration.DEFAULT_ENABLE_EDGE_PROPERTIES; + enableSpatialIndex = GraphStoreConfiguration.DEFAULT_ENABLE_SPATIAL_INDEX; + enableParallelEdgesSameType = GraphStoreConfiguration.DEFAULT_ENABLE_PARALLEL_EDGES_SAME_TYPE; + } + + public ConfigurationImpl(Configuration configuration) { + nodeIdType = configuration.getNodeIdType(); + edgeIdType = configuration.getEdgeIdType(); + edgeLabelType = configuration.getEdgeLabelType(); + edgeWeightType = configuration.getEdgeWeightType(); + timeRepresentation = configuration.getTimeRepresentation(); + edgeWeightColumn = configuration.getEdgeWeightColumn(); + enableAutoLocking = configuration.isEnableAutoLocking(); + enableAutoEdgeTypeRegistration = configuration.isEnableAutoEdgeTypeRegistration(); + enableIndexNodes = configuration.isEnableIndexNodes(); + enableIndexEdges = configuration.isEnableIndexEdges(); + enableIndexTime = configuration.isEnableIndexTime(); + enableObservers = configuration.isEnableObservers(); + enableNodeProperties = configuration.isEnableNodeProperties(); + enableEdgeProperties = configuration.isEnableEdgeProperties(); + enableSpatialIndex = configuration.isEnableSpatialIndex(); + enableParallelEdgesSameType = configuration.isEnableParallelEdgesSameType(); + } + + public Configuration toConfiguration() { + return new ConfigurationProxy(this); + } + + public Class getNodeIdType() { + return nodeIdType; + } + + public Class getEdgeIdType() { + return edgeIdType; + } + + public Class getEdgeLabelType() { + return edgeLabelType; + } + + public Class getEdgeWeightType() { + return edgeWeightType; + } + + public TimeRepresentation getTimeRepresentation() { + return timeRepresentation; + } + + public boolean isEdgeWeightColumn() { + return edgeWeightColumn; + } + + public boolean isEnableAutoLocking() { + return enableAutoLocking; + } + + public boolean isEnableAutoEdgeTypeRegistration() { + return enableAutoEdgeTypeRegistration; + } + + public boolean isEnableIndexNodes() { + return enableIndexNodes; + } + + public boolean isEnableIndexEdges() { + return enableIndexEdges; + } + + public boolean isEnableIndexTime() { + return enableIndexTime; + } + + public boolean isEnableObservers() { + return enableObservers; + } + + public boolean isEnableNodeProperties() { + return enableNodeProperties; + } + + public boolean isEnableEdgeProperties() { + return enableEdgeProperties; + } + + public boolean isEnableSpatialIndex() { + return enableSpatialIndex; + } + + public boolean isEnableParallelEdgesSameType() { + return enableParallelEdgesSameType; + } + + // Used to return a Configuration instance + private static class ConfigurationProxy extends Configuration { + + private ConfigurationProxy(ConfigurationImpl impl) { + super(impl); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ConfigurationImpl)) { + return false; + } + + ConfigurationImpl that = (ConfigurationImpl) o; + + if (isEdgeWeightColumn() != that.isEdgeWeightColumn()) { + return false; + } + if (isEnableAutoLocking() != that.isEnableAutoLocking()) { + return false; + } + if (isEnableAutoEdgeTypeRegistration() != that.isEnableAutoEdgeTypeRegistration()) { + return false; + } + if (isEnableIndexNodes() != that.isEnableIndexNodes()) { + return false; + } + if (isEnableIndexEdges() != that.isEnableIndexEdges()) { + return false; + } + if (isEnableIndexTime() != that.isEnableIndexTime()) { + return false; + } + if (isEnableObservers() != that.isEnableObservers()) { + return false; + } + if (isEnableNodeProperties() != that.isEnableNodeProperties()) { + return false; + } + if (isEnableEdgeProperties() != that.isEnableEdgeProperties()) { + return false; + } + if (isEnableSpatialIndex() != that.isEnableSpatialIndex()) { + return false; + } + if (isEnableParallelEdgesSameType() != that.isEnableParallelEdgesSameType()) { + return false; + } + if (!getNodeIdType().equals(that.getNodeIdType())) { + return false; + } + if (!getEdgeIdType().equals(that.getEdgeIdType())) { + return false; + } + if (!getEdgeLabelType().equals(that.getEdgeLabelType())) { + return false; + } + if (!getEdgeWeightType().equals(that.getEdgeWeightType())) { + return false; + } + return getTimeRepresentation() == that.getTimeRepresentation(); + } + + @Override + public int hashCode() { + int result = getNodeIdType().hashCode(); + result = 31 * result + getEdgeIdType().hashCode(); + result = 31 * result + getEdgeLabelType().hashCode(); + result = 31 * result + getEdgeWeightType().hashCode(); + result = 31 * result + getTimeRepresentation().hashCode(); + result = 31 * result + (isEdgeWeightColumn() ? 1 : 0); + result = 31 * result + (isEnableAutoLocking() ? 1 : 0); + result = 31 * result + (isEnableAutoEdgeTypeRegistration() ? 1 : 0); + result = 31 * result + (isEnableIndexNodes() ? 1 : 0); + result = 31 * result + (isEnableIndexEdges() ? 1 : 0); + result = 31 * result + (isEnableIndexTime() ? 1 : 0); + result = 31 * result + (isEnableObservers() ? 1 : 0); + result = 31 * result + (isEnableNodeProperties() ? 1 : 0); + result = 31 * result + (isEnableEdgeProperties() ? 1 : 0); + result = 31 * result + (isEnableSpatialIndex() ? 1 : 0); + result = 31 * result + (isEnableParallelEdgesSameType() ? 1 : 0); + return result; + } +} diff --git a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java index e782383f..a78eab91 100644 --- a/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java +++ b/src/main/java/org/gephi/graph/impl/DefaultColumnsImpl.java @@ -12,8 +12,8 @@ public class DefaultColumnsImpl implements GraphModel.DefaultColumns { protected final GraphStore store; // Default columns (initialised at store creation) - protected TableDefaultColumns nodeDefaultColumns; - protected TableDefaultColumns edgeDefaultColumns; + protected final TableDefaultColumns nodeDefaultColumns; + protected final TableDefaultColumns edgeDefaultColumns; // Extra columns (temporary solution, until they are fully added as normal // columns) @@ -37,9 +37,23 @@ public DefaultColumnsImpl(GraphStore store) { null, Origin.PROPERTY, false, true); } - public void resetConfiguration() { - this.nodeDefaultColumns = new TableDefaultColumns<>(store.nodeTable); - this.edgeDefaultColumns = new TableDefaultColumns<>(store.edgeTable); + // Used by serialization + protected ColumnImpl getColumn(TableImpl table, int storeId) { + TableDefaultColumns defaultColumns = table.isNodeTable() ? nodeDefaultColumns + : edgeDefaultColumns; + switch (storeId) { + case GraphStoreConfiguration.ELEMENT_ID_INDEX: + return defaultColumns.id; + case GraphStoreConfiguration.ELEMENT_LABEL_INDEX: + return defaultColumns.label; + case GraphStoreConfiguration.ELEMENT_TIMESET_INDEX: + return defaultColumns.timeset; + } + + if (table.isEdgeTable() && storeId == GraphStoreConfiguration.EDGE_WEIGHT_INDEX) { + return store.edgeTable.getColumn(storeId); + } + return null; } @Override diff --git a/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/src/main/java/org/gephi/graph/impl/EdgeImpl.java index 0f9b4667..53cf4b81 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -18,15 +18,11 @@ import java.awt.Color; import java.util.Map; import org.gephi.graph.api.Column; -import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeProperties; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Table; -import org.gephi.graph.api.types.IntervalMap; -import org.gephi.graph.api.types.TimeMap; -import org.gephi.graph.api.types.TimestampMap; import static org.gephi.graph.impl.GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; public class EdgeImpl extends ElementImpl implements Edge { @@ -57,7 +53,8 @@ public EdgeImpl(Object id, GraphStore graphStore, NodeImpl source, NodeImpl targ this.target = target; this.flags = (byte) (directed ? 1 : 0); this.type = type; - this.properties = GraphStoreConfiguration.ENABLE_EDGE_PROPERTIES ? new EdgePropertiesImpl() : null; + this.properties = graphStore == null || graphStore.configuration.isEnableEdgeProperties() + ? new EdgePropertiesImpl() : null; if (graphStore == null || graphStore.configuration.getEdgeWeightType().equals(Double.class)) { this.attributes.setAttribute(GraphStoreConfiguration.EDGE_WEIGHT_INDEX, weight); } diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 3707b6fc..5731d5f1 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -49,6 +49,8 @@ public class EdgeStore implements Collection, EdgeIterable { protected final GraphViewStore viewStore; // Spatial index protected final SpatialIndexImpl spatialIndex; + // Configuration + protected final ConfigurationImpl configuration; // Data protected int size; protected int garbageSize; @@ -70,15 +72,17 @@ public EdgeStore() { this.viewStore = null; this.version = null; this.spatialIndex = null; + this.configuration = new ConfigurationImpl(); } - public EdgeStore(final EdgeTypeStore edgeTypeStore, final SpatialIndexImpl spatialIndex, final GraphLockImpl lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { + public EdgeStore(final EdgeTypeStore edgeTypeStore, final SpatialIndexImpl spatialIndex, final ConfigurationImpl configuration, final GraphLockImpl lock, final GraphViewStore viewStore, final GraphVersion graphVersion) { initStore(); this.lock = lock; this.edgeTypeStore = edgeTypeStore; this.viewStore = viewStore; this.version = graphVersion; this.spatialIndex = spatialIndex; + this.configuration = configuration == null ? new ConfigurationImpl() : configuration; } protected static long getLongId(NodeImpl source, NodeImpl target, boolean directed) { @@ -574,7 +578,7 @@ public boolean setEdgeType(final Edge e, int type) { long longId = getLongId(edge.source, edge.target, edge.isDirected()); int[] newDicoValue = newDico.get(longId); - if (newDicoValue != null && !GraphStoreConfiguration.ENABLE_PARALLEL_EDGES) { + if (newDicoValue != null && !configuration.isEnableParallelEdgesSameType()) { return false; } @@ -693,10 +697,13 @@ public boolean add(final Edge e) { Long2ObjectOpenCustomHashMap dico = longDictionary[type]; long longId = getLongId(source, target, directed); int[] dicoValue = dico.get(longId); - if (dicoValue != null && !GraphStoreConfiguration.ENABLE_PARALLEL_EDGES) { + if (dicoValue != null && !configuration.isEnableParallelEdgesSameType()) { return false; } + if (edgeTypeStore != null) { + edgeTypeStore.registerEdgeType(type); + } incrementVersion(); if (garbageSize > 0) { diff --git a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java index bd052c92..884fc17d 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java @@ -34,17 +34,17 @@ public class EdgeTypeStore { // Config public final static int MAX_SIZE = 65534; // Data - protected final Configuration configuration; + protected final ConfigurationImpl configuration; protected final Object2ShortMap labelMap; protected final Short2ObjectMap idMap; protected final ShortSortedSet garbageQueue; protected int length; public EdgeTypeStore() { - this(new Configuration()); + this(new ConfigurationImpl()); } - public EdgeTypeStore(Configuration config) { + public EdgeTypeStore(ConfigurationImpl config) { if (MAX_SIZE >= Short.MAX_VALUE - Short.MIN_VALUE + 1) { throw new RuntimeException("Edge Type Store size can't exceed 65534"); } @@ -78,10 +78,11 @@ public Object getLabel(final int id) { public void registerEdgeType(int type) { if (!contains(type)) { - if (GraphStoreConfiguration.ENABLE_AUTO_TYPE_REGISTRATION) { + if (configuration.isEnableAutoEdgeTypeRegistration()) { addType(String.valueOf(type), type); } else { - throw new RuntimeException("The type doesn't exist"); + throw new UnsupportedOperationException( + "The type " + type + " doesn't exist, and edge type auto registration is disabled (from Configuration)"); } } } diff --git a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java index c59887ad..41ee1cf0 100644 --- a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java @@ -97,14 +97,17 @@ public void copyNodes(Node[] nodes) { if (store.getNode(node.getId()) == null) { Node nodeCopy = factory.newNode(node.getId()); + // Label + copyLabel(node, nodeCopy); + // Time set copyTimeSet(node, nodeCopy); // Properties - copyNodeProperties(node, nodeCopy); - - // Text properties - copyTextProperties(node.getTextProperties(), nodeCopy.getTextProperties()); + if (store.configuration.isEnableNodeProperties()) { + copyNodeProperties(node, nodeCopy); + copyTextProperties(node.getTextProperties(), nodeCopy.getTextProperties()); + } // Attributes copyAttributes(sourceStore.nodeTable, nodeTable, node, nodeCopy); @@ -122,6 +125,9 @@ public void copyNodes(Node[] nodes) { Edge edgeCopy = factory.newEdge(edge.getId(), source, target, edge.getType(), 0.0, edge.isDirected()); + // Label + copyLabel(edge, edgeCopy); + // Time set copyTimeSet(edge, edgeCopy); @@ -129,10 +135,10 @@ public void copyNodes(Node[] nodes) { copyEdgeWeight(edge, edgeCopy); // Properties - copyEdgeProperties(edge, edgeCopy); - - // Text properties - copyTextProperties(edge.getTextProperties(), edgeCopy.getTextProperties()); + if (store.configuration.isEnableEdgeProperties()) { + copyEdgeProperties(edge, edgeCopy); + copyTextProperties(edge.getTextProperties(), edgeCopy.getTextProperties()); + } // Attributes copyAttributes(sourceStore.edgeTable, edgeTable, edge, edgeCopy); @@ -164,13 +170,15 @@ private void copyNodeProperties(Node node, Node nodeCopy) { nodeCopy.setPosition(node.x(), node.y(), node.z()); nodeCopy.setColor(node.getColor()); nodeCopy.setFixed(node.isFixed()); - nodeCopy.setLabel(node.getLabel()); nodeCopy.setSize(node.size()); } + private void copyLabel(Element element, Element elementCopy) { + elementCopy.setLabel(element.getLabel()); + } + private void copyEdgeProperties(Edge edge, Edge edgeCopy) { edgeCopy.setColor(edge.getColor()); - edgeCopy.setLabel(edge.getLabel()); } private void copyTextProperties(TextProperties text, TextProperties textCopy) { @@ -195,7 +203,7 @@ private void copyColumns(TableImpl sourceTable, TableImpl destTable) { } private void copyAttributes(TableImpl sourceTable, TableImpl destTable, Element element, Element elementCopy) { - TimeRepresentation tr = sourceTable.store.configuration.getTimeRepresentation(); + TimeRepresentation tr = sourceTable.store.graphStore.configuration.getTimeRepresentation(); for (Column col : sourceTable.toArray()) { if (!col.isProperty()) { Column colCopy = destTable.getColumn(col.getId()); @@ -245,10 +253,45 @@ private void verifyElement(ElementImpl elementImpl) { private void verifyCompatibility(GraphStore sourceStore) { // Verify configuration - Configuration destConfig = store.configuration; - Configuration sourceConfig = sourceStore.configuration; - if (!destConfig.equals(sourceConfig)) { - throw new RuntimeException("The configurations don't match"); + ConfigurationImpl destConfig = store.configuration; + ConfigurationImpl sourceConfig = sourceStore.configuration; + + // Time representation + if (!destConfig.getTimeRepresentation().equals(sourceConfig.getTimeRepresentation())) { + throw new RuntimeException("The time representations doesn't match, source: " + sourceConfig + .getTimeRepresentation() + ", destination: " + destConfig.getTimeRepresentation()); + } + + // Node id type + if (!destConfig.getNodeIdType().equals(sourceConfig.getNodeIdType())) { + throw new RuntimeException("The node id type doesn't match, source: " + sourceConfig + .getNodeIdType() + ", destination: " + destConfig.getNodeIdType()); + } + + // Edge id type + if (!destConfig.getEdgeIdType().equals(sourceConfig.getEdgeIdType())) { + throw new RuntimeException("The edge id type doesn't match, source: " + sourceConfig + .getEdgeIdType() + ", destination: " + destConfig.getEdgeIdType()); + } + + // Edge weight type + if (!destConfig.getEdgeWeightType().equals(sourceConfig.getEdgeWeightType())) { + throw new RuntimeException("The edge weight type doesn't match, source: " + sourceConfig + .getEdgeWeightType() + ", destination: " + destConfig.getEdgeWeightType()); + } + + // Edge label type + if (!destConfig.getEdgeLabelType().equals(sourceConfig.getEdgeLabelType())) { + throw new RuntimeException("The edge label type doesn't match, source: " + sourceConfig + .getEdgeLabelType() + ", destination: " + destConfig.getEdgeLabelType()); + } + + // Parallel edges + if (destConfig.isEnableParallelEdgesSameType() != sourceConfig.isEnableParallelEdgesSameType()) { + throw new RuntimeException( + "The parallel edges of same type configuration doesn't match, source: " + sourceConfig + .isEnableParallelEdgesSameType() + ", destination: " + destConfig + .isEnableParallelEdgesSameType()); } // Verify node table diff --git a/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java b/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java index c57649b1..e54ccaec 100644 --- a/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java @@ -30,8 +30,8 @@ protected enum AssignConfiguration { protected final AtomicInteger NODE_IDS = new AtomicInteger(); protected final AtomicInteger EDGE_IDS = new AtomicInteger(); // Config - protected AssignConfiguration nodeAssignConfiguration; - protected AssignConfiguration edgeAssignConfiguration; + protected final AssignConfiguration nodeAssignConfiguration; + protected final AssignConfiguration edgeAssignConfiguration; // Store protected final GraphStore store; @@ -207,13 +207,6 @@ public boolean deepEquals(GraphFactoryImpl obj) { return true; } - public void resetConfiguration() { - this.nodeAssignConfiguration = getAssignConfiguration(AttributeUtils - .getStandardizedType(store.configuration.getNodeIdType())); - this.edgeAssignConfiguration = getAssignConfiguration(AttributeUtils - .getStandardizedType(store.configuration.getEdgeIdType())); - } - protected final AssignConfiguration getAssignConfiguration(Class type) { if (type.equals(Integer.class)) { return AssignConfiguration.INTEGER; diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 143ef225..7829777e 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -48,18 +48,18 @@ public class GraphModelImpl implements GraphModel { - protected final Configuration configuration; + protected final ConfigurationImpl configuration; protected final GraphStore store; protected final GraphBridgeImpl graphBridge; public GraphModelImpl() { - this(new Configuration()); + this(Configuration.builder().build()); } public GraphModelImpl(Configuration config) { checkValidConfiguration(config); - configuration = config.copy(); + configuration = new ConfigurationImpl(config); store = new GraphStore(this); graphBridge = new GraphBridgeImpl(store); } @@ -376,6 +376,9 @@ public TimeIndex getEdgeTimeIndex(GraphView view) { @Override public SpatialIndex getSpatialIndex() { + if (!configuration.isEnableSpatialIndex()) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } return store.spatialIndex; } @@ -438,105 +441,13 @@ public Interval getTimeBounds(GraphView view) { @Override public Configuration getConfiguration() { - return configuration.copy(); + return configuration.toConfiguration(); } @Override public void setConfiguration(Configuration config) { - checkValidConfiguration(config); - - store.autoWriteLock(); - try { - if (store.getNodeCount() > 0 || !store.attributes.isEmpty() || store.nodeTable - .countColumns() != GraphStoreConfiguration.NODE_DEFAULT_COLUMNS || store.edgeTable - .countColumns() != GraphStoreConfiguration.EDGE_DEFAULT_COLUMNS || store.edgeTypeStore - .size() > 1) { - throw new IllegalStateException("The store should be empty when modifying the configuration"); - } - - if (!config.getNodeIdType().equals(configuration.getNodeIdType())) { - TableImpl nodeTable = store.nodeTable; - nodeTable.store.removeColumn(GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID); - nodeTable.store.addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID, - config.getNodeIdType(), "Id", null, Origin.PROPERTY, false, true)); - configuration.setNodeIdType(config.getNodeIdType()); - } - - if (!config.getEdgeIdType().equals(configuration.getEdgeIdType())) { - TableImpl edgeTable = store.edgeTable; - edgeTable.store.removeColumn(GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID); - edgeTable.store.addColumn(new ColumnImpl(edgeTable, GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID, - config.getEdgeIdType(), "Id", null, Origin.PROPERTY, false, true)); - configuration.setEdgeIdType(config.getEdgeIdType()); - } - - if (!config.getEdgeLabelType().equals(configuration.getEdgeLabelType())) { - configuration.setEdgeLabelType(config.getEdgeLabelType()); - } - - // Replace dynamic timeset columns if time representation changes: - if (!config.getTimeRepresentation().equals(configuration.getTimeRepresentation())) { - TableImpl nodeTable = store.nodeTable; - nodeTable.removeColumn(GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID); - TableImpl edgeTable = store.edgeTable; - edgeTable.removeColumn(GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID); - - if (config.getTimeRepresentation().equals(TimeRepresentation.TIMESTAMP)) { - nodeTable.store - .addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, - TimestampSet.class, "Timestamp", null, Origin.PROPERTY, false, false)); - edgeTable.store - .addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, - TimestampSet.class, "Timestamp", null, Origin.PROPERTY, false, false)); - } else { - nodeTable.store - .addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, - IntervalSet.class, "Interval", null, Origin.PROPERTY, false, false)); - edgeTable.store - .addColumn(new ColumnImpl(nodeTable, GraphStoreConfiguration.ELEMENT_TIMESET_COLUMN_ID, - IntervalSet.class, "Interval", null, Origin.PROPERTY, false, false)); - } - configuration.setTimeRepresentation(config.getTimeRepresentation()); - store.timeStore.resetConfiguration(); - } - - // Change whether edge weight column - final boolean edgeWeightIndexed = AttributeUtils.isSimpleType(config.getEdgeWeightType()); - - if (!config.getEdgeWeightColumn().equals(configuration.getEdgeWeightColumn())) { - TableImpl edgeTable = store.edgeTable; - if (config.getEdgeWeightColumn()) { - edgeTable.store.garbageQueue - .add(edgeTable.store.intToShort(GraphStoreConfiguration.EDGE_WEIGHT_INDEX)); - edgeTable.store.addColumn(new ColumnImpl(edgeTable, GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID, - config.getEdgeWeightType(), "Weight", null, Origin.PROPERTY, edgeWeightIndexed, false)); - } else { - edgeTable.removeColumn(GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID); - edgeTable.store.garbageQueue - .remove(edgeTable.store.intToShort(GraphStoreConfiguration.EDGE_WEIGHT_INDEX)); - } - } - - // Change weight column type: - if (!config.getEdgeWeightType().equals(configuration.getEdgeWeightType())) { - TableImpl edgeTable = store.edgeTable; - - Class newWeightType = config.getEdgeWeightType(); - if (config.getEdgeWeightColumn()) { - edgeTable.removeColumn(GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID); - - edgeTable.store.addColumn(new ColumnImpl(edgeTable, GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID, - newWeightType, "Weight", null, Origin.PROPERTY, edgeWeightIndexed, false)); - } - - configuration.setEdgeWeightType(newWeightType); - } - - store.factory.resetConfiguration(); - store.defaultColumns.resetConfiguration(); - } finally { - store.autoWriteUnlock(); - } + throw new UnsupportedOperationException( + "No longer supported. Configuration is immutable and needs to be passed at GraphModel creation time"); } @Override @@ -595,6 +506,9 @@ private void checkGraphObserver(GraphObserver observer) { } private void checkValidConfiguration(Configuration config) { + if (config == null) { + throw new NullPointerException("Configuration cannot be null, use Configuration.builder().build() instead"); + } Class edgeWeightType = config.getEdgeWeightType(); if (edgeWeightType.equals(Double.class)) { return;// Double is always allowed diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index aa57984b..3038d5c4 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -16,7 +16,6 @@ package org.gephi.graph.impl; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import java.time.ZoneId; import java.util.ArrayList; import java.util.Collection; @@ -24,7 +23,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.DirectedSubgraph; @@ -48,7 +46,7 @@ public class GraphStore implements DirectedGraph, DirectedSubgraph { protected final GraphModelImpl graphModel; - protected final Configuration configuration; + protected final ConfigurationImpl configuration; // Stores protected final NodeStore nodeStore; protected final EdgeStore edgeStore; @@ -79,28 +77,36 @@ public class GraphStore implements DirectedGraph, DirectedSubgraph { protected final DefaultColumnsImpl defaultColumns; public GraphStore() { - this(null); + this(null, new ConfigurationImpl()); } public GraphStore(GraphModelImpl model) { - configuration = model != null ? model.configuration : new Configuration(); + this(model, model.configuration); + } + + protected GraphStore(GraphModelImpl model, Configuration config) { + this(model, new ConfigurationImpl(config)); + } + + protected GraphStore(GraphModelImpl model, ConfigurationImpl config) { + configuration = config; graphModel = model; lock = new GraphLockImpl(); edgeTypeStore = new EdgeTypeStore(); mainGraphView = new MainGraphView(); viewStore = new GraphViewStore(this); - version = GraphStoreConfiguration.ENABLE_OBSERVERS ? new GraphVersion(this) : null; - observers = GraphStoreConfiguration.ENABLE_OBSERVERS ? new ArrayList<>() : null; - spatialIndex = GraphStoreConfiguration.ENABLE_SPATIAL_INDEX ? new SpatialIndexImpl(this) : null; - edgeStore = new EdgeStore(edgeTypeStore, spatialIndex, - GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? lock : null, viewStore, - GraphStoreConfiguration.ENABLE_OBSERVERS ? version : null); - nodeStore = new NodeStore(edgeStore, spatialIndex, GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? lock : null, - viewStore, GraphStoreConfiguration.ENABLE_OBSERVERS ? version : null); - nodeTable = new TableImpl<>(this, Node.class, GraphStoreConfiguration.ENABLE_INDEX_NODES); - edgeTable = new TableImpl<>(this, Edge.class, GraphStoreConfiguration.ENABLE_INDEX_EDGES); - timeStore = new TimeStore(this, GraphStoreConfiguration.ENABLE_INDEX_TIMESTAMP); + version = configuration.isEnableObservers() ? new GraphVersion(this) : null; + observers = configuration.isEnableObservers() ? new ArrayList<>() : null; + spatialIndex = configuration.isEnableSpatialIndex() ? new SpatialIndexImpl(this) : null; + edgeStore = new EdgeStore(edgeTypeStore, spatialIndex, configuration, + configuration.isEnableAutoLocking() ? lock : null, viewStore, + configuration.isEnableObservers() ? version : null); + nodeStore = new NodeStore(edgeStore, spatialIndex, configuration.isEnableAutoLocking() ? lock : null, viewStore, + configuration.isEnableObservers() ? version : null); + nodeTable = new TableImpl<>(this, Node.class); + edgeTable = new TableImpl<>(this, Edge.class); + timeStore = new TimeStore(this, configuration.isEnableIndexTime()); attributes = new GraphAttributesImpl(); factory = new GraphFactoryImpl(this); timeFormat = GraphStoreConfiguration.DEFAULT_TIME_FORMAT; @@ -132,10 +138,9 @@ public GraphStore(GraphModelImpl model) { IntervalSet.class, "Interval", null, Origin.PROPERTY, false, false)); } } - if (configuration.getEdgeWeightColumn()) { - final boolean edgeWeightIndexed = AttributeUtils.isSimpleType(configuration.getEdgeWeightType()); + if (configuration.isEdgeWeightColumn()) { edgeTable.store.addColumn(new ColumnImpl(edgeTable, GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID, - configuration.getEdgeWeightType(), "Weight", null, Origin.PROPERTY, edgeWeightIndexed, false)); + configuration.getEdgeWeightType(), "Weight", null, Origin.PROPERTY, false, false)); } else { edgeTable.store.length++; } @@ -166,9 +171,6 @@ public boolean addAllNodes(final Collection nodes) { public boolean addEdge(final Edge edge) { autoWriteLock(); try { - if (edgeTypeStore != null) { - edgeTypeStore.registerEdgeType(edge.getType()); - } return edgeStore.add(edge); } finally { autoWriteUnlock(); @@ -179,11 +181,6 @@ public boolean addEdge(final Edge edge) { public boolean addAllEdges(Collection edges) { autoWriteLock(); try { - for (Edge edge : edges) { - if (edgeTypeStore != null) { - edgeTypeStore.registerEdgeType(edge.getType()); - } - } return edgeStore.addAll(edges); } finally { autoWriteUnlock(); @@ -705,31 +702,31 @@ public GraphLockImpl getLock() { } protected void autoReadLock() { - if (GraphStoreConfiguration.ENABLE_AUTO_LOCKING) { + if (configuration.isEnableAutoLocking()) { readLock(); } } protected void autoReadUnlock() { - if (GraphStoreConfiguration.ENABLE_AUTO_LOCKING) { + if (configuration.isEnableAutoLocking()) { readUnlock(); } } protected void autoReadUnlockAll() { - if (GraphStoreConfiguration.ENABLE_AUTO_LOCKING) { + if (configuration.isEnableAutoLocking()) { readUnlockAll(); } } protected void autoWriteLock() { - if (GraphStoreConfiguration.ENABLE_AUTO_LOCKING) { + if (configuration.isEnableAutoLocking()) { writeLock(); } } protected void autoWriteUnlock() { - if (GraphStoreConfiguration.ENABLE_AUTO_LOCKING) { + if (configuration.isEnableAutoLocking()) { writeUnlock(); } } @@ -832,13 +829,11 @@ protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator } protected EdgeIterableWrapper getEdgeIterableWrapper(Iterator edgeIterator, boolean blocking) { - return new EdgeIterableWrapper(edgeIterator, - (blocking && GraphStoreConfiguration.ENABLE_AUTO_LOCKING) ? lock : null); + return new EdgeIterableWrapper(edgeIterator, (blocking && configuration.isEnableAutoLocking()) ? lock : null); } protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator, boolean blocking) { - return new NodeIterableWrapper(nodeIterator, - (blocking && GraphStoreConfiguration.ENABLE_AUTO_LOCKING) ? lock : null); + return new NodeIterableWrapper(nodeIterator, (blocking && configuration.isEnableAutoLocking()) ? lock : null); } public int deepHashCode() { diff --git a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java index 6c8eba6c..e57632a2 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java +++ b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java @@ -16,7 +16,6 @@ package org.gephi.graph.impl; import java.time.ZoneId; -import java.time.ZonedDateTime; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.api.TimeRepresentation; @@ -24,16 +23,17 @@ public final class GraphStoreConfiguration { // Features - public static final boolean ENABLE_AUTO_LOCKING = true; - public static final boolean ENABLE_AUTO_TYPE_REGISTRATION = true; - public static final boolean ENABLE_INDEX_NODES = true; - public static final boolean ENABLE_INDEX_EDGES = true; - public static final boolean ENABLE_INDEX_TIMESTAMP = true; - public static final boolean ENABLE_OBSERVERS = true; - public static final boolean ENABLE_NODE_PROPERTIES = true; - public static final boolean ENABLE_EDGE_PROPERTIES = true; - public static final boolean ENABLE_PARALLEL_EDGES = true; - public static final boolean ENABLE_SPATIAL_INDEX = false; + public static final boolean DEFAULT_ENABLE_AUTO_LOCKING = true; + public static final boolean DEFAULT_ENABLE_AUTO_EDGE_TYPE_REGISTRATION = true; + public static final boolean DEFAULT_ENABLE_INDEX_NODES = true; + public static final boolean DEFAULT_ENABLE_INDEX_EDGES = true; + public static final boolean DEFAULT_ENABLE_INDEX_TIME = true; + public static final boolean DEFAULT_ENABLE_OBSERVERS = true; + public static final boolean DEFAULT_ENABLE_NODE_PROPERTIES = true; + public static final boolean DEFAULT_ENABLE_EDGE_PROPERTIES = true; + public static final boolean DEFAULT_ENABLE_SPATIAL_INDEX = false; + public static final boolean DEFAULT_ENABLE_EDGE_WEIGHT_COLUMN = true; + public static final boolean DEFAULT_ENABLE_PARALLEL_EDGES_SAME_TYPE = true; // NodeStore public final static int NODESTORE_BLOCK_SIZE = 5000; public final static int NODESTORE_DEFAULT_BLOCKS = 10; diff --git a/src/main/java/org/gephi/graph/impl/NodeImpl.java b/src/main/java/org/gephi/graph/impl/NodeImpl.java index db350c78..e3c44777 100644 --- a/src/main/java/org/gephi/graph/impl/NodeImpl.java +++ b/src/main/java/org/gephi/graph/impl/NodeImpl.java @@ -36,7 +36,8 @@ public class NodeImpl extends ElementImpl implements Node { public NodeImpl(Object id, GraphStore graphStore) { super(id, graphStore); checkIdType(id); - this.properties = GraphStoreConfiguration.ENABLE_NODE_PROPERTIES ? new NodePropertiesImpl() : null; + this.properties = graphStore == null || graphStore.configuration.isEnableNodeProperties() + ? new NodePropertiesImpl() : null; } public NodeImpl(Object id) { diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index d80b8134..f35a8312 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -241,29 +241,58 @@ public void serializeGraphModel(DataOutput out, GraphModelImpl model) throws IOE public GraphModelImpl deserializeGraphModel(DataInput is) throws IOException, ClassNotFoundException { readVersion = (Float) deserialize(is); - Configuration config = (Configuration) deserialize(is); - model = new GraphModelImpl(config); + ConfigurationImpl config = (ConfigurationImpl) deserialize(is); + model = new GraphModelImpl(config.toConfiguration()); deserialize(is); - model.store.defaultColumns.resetConfiguration(); return model; } public GraphModelImpl deserializeGraphModel(DataInput is, GraphModel graphModel) throws IOException, ClassNotFoundException { model = (GraphModelImpl) graphModel; readVersion = (Float) deserialize(is); - Configuration config = (Configuration) deserialize(is); - model.setConfiguration(config); + ConfigurationImpl config = (ConfigurationImpl) deserialize(is); + verifyCompatibility(config, model.configuration); deserialize(is); - model.store.defaultColumns.resetConfiguration(); return model; } + private void verifyCompatibility(ConfigurationImpl readConfig, ConfigurationImpl modelConfig) { + // Time representation + if (!readConfig.getTimeRepresentation().equals(modelConfig.getTimeRepresentation())) { + throw new RuntimeException("The time representations doesn't match, read: " + readConfig + .getTimeRepresentation() + ", model: " + modelConfig.getTimeRepresentation()); + } + + // Node id type + if (!readConfig.getNodeIdType().equals(modelConfig.getNodeIdType())) { + throw new RuntimeException("The node id type doesn't match, read: " + readConfig + .getNodeIdType() + ", model: " + modelConfig.getNodeIdType()); + } + + // Edge id type + if (!readConfig.getEdgeIdType().equals(modelConfig.getEdgeIdType())) { + throw new RuntimeException("The edge id type doesn't match, read: " + readConfig + .getEdgeIdType() + ", model: " + modelConfig.getEdgeIdType()); + } + + // Edge weight type + if (!readConfig.getEdgeWeightType().equals(modelConfig.getEdgeWeightType())) { + throw new RuntimeException("The edge weight type doesn't match, read: " + readConfig + .getEdgeWeightType() + ", model: " + modelConfig.getEdgeWeightType()); + } + + // Edge label type + if (!readConfig.getEdgeLabelType().equals(modelConfig.getEdgeLabelType())) { + throw new RuntimeException("The edge label type doesn't match, read: " + readConfig + .getEdgeLabelType() + ", model: " + modelConfig.getEdgeLabelType()); + } + } + public GraphModelImpl deserializeGraphModelWithoutVersionPrefix(DataInput is, float version) throws IOException, ClassNotFoundException { readVersion = version; - Configuration config = (Configuration) deserialize(is); - model = new GraphModelImpl(config); + ConfigurationImpl config = (ConfigurationImpl) deserialize(is); + model = new GraphModelImpl(config.toConfiguration()); deserialize(is); - model.store.defaultColumns.resetConfiguration(); return model; } @@ -552,24 +581,14 @@ private ColumnImpl deserializeColumn(final DataInput is, TableImpl table) throws boolean readOnly = (Boolean) deserialize(is); Estimator estimator = (Estimator) deserialize(is); - ColumnImpl column = new ColumnImpl(table, (String) id, typeClass, title, defaultValue, origin, indexed, - readOnly); - column.storeId = storeId; - if (estimator != null) { - column.setEstimator(estimator); + ColumnImpl column = model.store.defaultColumns.getColumn(table, storeId); + if (column == null) { + column = new ColumnImpl(table, (String) id, typeClass, title, defaultValue, origin, indexed, readOnly); + column.storeId = storeId; } - // Make sure configured types match the deserialized column types: - if (Edge.class.equals(table.getElementClass())) { - if (id.equals(GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID)) { - table.store.configuration.setEdgeWeightType(typeClass); - } else if (id.equals(GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID)) { - table.store.configuration.setEdgeIdType(typeClass); - } - } else if (Node.class.equals(table.getElementClass())) { - if (id.equals(GraphStoreConfiguration.ELEMENT_ID_COLUMN_ID)) { - table.store.configuration.setNodeIdType(typeClass); - } + if (estimator != null) { + column.setEstimator(estimator); } return column; @@ -689,8 +708,10 @@ private void serializeGraphStoreConfiguration(final DataOutput out) throws IOExc out.write(GRAPH_STORE_CONFIGURATION); serialize(out, GraphStoreConfiguration.ENABLE_ELEMENT_LABEL); serialize(out, GraphStoreConfiguration.ENABLE_ELEMENT_TIME_SET); - serialize(out, GraphStoreConfiguration.ENABLE_NODE_PROPERTIES); - serialize(out, GraphStoreConfiguration.ENABLE_EDGE_PROPERTIES); + // Was GraphStoreConfiguration.ENABLE_NODE_PROPERTIES + serialize(out, true); + // Was GraphStoreConfiguration.ENABLE_EDGE_PROPERTIES + serialize(out, true); } private GraphStoreConfigurationVersion deserializeGraphStoreConfiguration(final DataInput is) throws IOException, ClassNotFoundException { @@ -1082,18 +1103,18 @@ private TimeStore deserializeTimeStore(final DataInput is) throws IOException, C } private void serializeConfiguration(final DataOutput out) throws IOException { - Configuration config = model.store.configuration; + ConfigurationImpl config = model.configuration; serialize(out, config.getNodeIdType()); serialize(out, config.getEdgeIdType()); serialize(out, config.getEdgeLabelType()); serialize(out, config.getEdgeWeightType()); serialize(out, config.getTimeRepresentation()); - serialize(out, config.getEdgeWeightColumn()); + serialize(out, config.isEdgeWeightColumn()); } - private Configuration deserializeConfiguration(final DataInput is) throws IOException, ClassNotFoundException { - Configuration config = new Configuration(); + private ConfigurationImpl deserializeConfiguration(final DataInput is) throws IOException, ClassNotFoundException { + Configuration.Builder config = Configuration.builder(); Class nodeIdType = (Class) deserialize(is); Class edgeIdType = (Class) deserialize(is); @@ -1101,17 +1122,17 @@ private Configuration deserializeConfiguration(final DataInput is) throws IOExce Class edgeWeightType = (Class) deserialize(is); TimeRepresentation timeRepresentation = (TimeRepresentation) deserialize(is); - config.setNodeIdType(nodeIdType); - config.setEdgeIdType(edgeIdType); - config.setEdgeLabelType(edgeLabelType); - config.setEdgeWeightType(edgeWeightType); - config.setTimeRepresentation(timeRepresentation); + config.nodeIdType(nodeIdType); + config.edgeIdType(edgeIdType); + config.edgeLabelType(edgeLabelType); + config.edgeWeightType(edgeWeightType); + config.timeRepresentation(timeRepresentation); if (readVersion >= 0.5) { Boolean edgeColumn = (Boolean) deserialize(is); - config.setEdgeWeightColumn(edgeColumn); + config.edgeWeightColumn(edgeColumn); } - return config; + return new ConfigurationImpl(config.build()); } private void serializeList(final DataOutput out, final List list) throws IOException { @@ -1584,8 +1605,8 @@ protected void serialize(final DataOutput out, final Object obj) throws IOExcept TimeStore b = (TimeStore) obj; out.write(TIME_STORE); serializeTimeStore(out); - } else if (obj instanceof Configuration) { - Configuration b = (Configuration) obj; + } else if (obj instanceof ConfigurationImpl) { + ConfigurationImpl b = (ConfigurationImpl) obj; out.write(CONFIGURATION); serializeConfiguration(out); } else if (obj instanceof Interval) { @@ -2323,4 +2344,4 @@ public GraphStoreConfigurationVersion(boolean enableElementLabel, boolean enable this.enableEdgeProperties = enableEdgeProperties; } } -} +} \ No newline at end of file diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index c8ac06e5..d217dee8 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -33,13 +33,21 @@ public class TableImpl implements Collection, Table { // Store protected final ColumnStore store; + // Configuration + protected final ConfigurationImpl configuration; - public TableImpl(Class elementType, boolean indexed) { - this(null, elementType, indexed); + public TableImpl(Class elementType) { + this(null, elementType); } - public TableImpl(GraphStore graphStore, Class elementType, boolean indexed) { - store = new ColumnStore<>(graphStore, elementType, indexed); + public TableImpl(GraphStore graphStore, Class elementType) { + store = new ColumnStore<>(graphStore, elementType); + if (graphStore == null) { + // Used for testing only + configuration = new ConfigurationImpl(); + } else { + configuration = graphStore.configuration; + } } @Override @@ -62,6 +70,7 @@ public Column addColumn(String id, String title, Class type, Origin origin, Obje checkValidId(id); checkSupportedTypes(type); checkDefaultValue(defaultValue, type); + checkCanIndex(indexed); type = AttributeUtils.getStandardizedType(type); if (defaultValue != null) { @@ -292,6 +301,19 @@ private void checkDefaultValue(Object defaultValue, Class type) { } } + private void checkCanIndex(boolean indexed) { + if (indexed && store.graphStore != null) { + if (!store.graphStore.configuration.isEnableIndexNodes() && isNodeTable()) { + throw new IllegalArgumentException( + "Can't use reverse index as node indexing is disabled (from Configuration)"); + } + if (!store.graphStore.configuration.isEnableIndexEdges() && isEdgeTable()) { + throw new IllegalArgumentException( + "Can't index edge table as edge indexing is disabled (from Configuration)"); + } + } + } + private void checkableTableObserver(TableObserver observer) { if (observer == null) { throw new NullPointerException(); diff --git a/src/main/java/org/gephi/graph/impl/TimeStore.java b/src/main/java/org/gephi/graph/impl/TimeStore.java index 56c2c0c1..5556e568 100644 --- a/src/main/java/org/gephi/graph/impl/TimeStore.java +++ b/src/main/java/org/gephi/graph/impl/TimeStore.java @@ -26,12 +26,12 @@ public class TimeStore { // Lock (optional) protected final TableLockImpl lock; // Store - protected TimeIndexStore nodeIndexStore; - protected TimeIndexStore edgeIndexStore; + protected final TimeIndexStore nodeIndexStore; + protected final TimeIndexStore edgeIndexStore; public TimeStore(GraphStore store, boolean indexed) { this.graphStore = store; - this.lock = GraphStoreConfiguration.ENABLE_AUTO_LOCKING ? new TableLockImpl() : null; + this.lock = store != null && store.configuration.isEnableAutoLocking() ? new TableLockImpl() : null; TimeRepresentation timeRepresentation = GraphStoreConfiguration.DEFAULT_TIME_REPRESENTATION; if (store != null) { @@ -46,18 +46,6 @@ public TimeStore(GraphStore store, boolean indexed) { } } - protected void resetConfiguration() { - if (graphStore != null) { - if (graphStore.configuration.getTimeRepresentation().equals(TimeRepresentation.INTERVAL)) { - nodeIndexStore = new IntervalIndexStore<>(Node.class, lock, nodeIndexStore.hasIndex()); - edgeIndexStore = new IntervalIndexStore<>(Edge.class, lock, edgeIndexStore.hasIndex()); - } else { - nodeIndexStore = new TimestampIndexStore<>(Node.class, lock, nodeIndexStore.hasIndex()); - edgeIndexStore = new TimestampIndexStore<>(Edge.class, lock, edgeIndexStore.hasIndex()); - } - } - } - public double getMin(Graph graph) { if (nodeIndexStore == null || edgeIndexStore == null) { // TODO: Manual calculation diff --git a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java index 646f06a4..6667ce6c 100644 --- a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java +++ b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java @@ -851,8 +851,8 @@ public void testgetTypeNameUnsupportedType() { @Test public void testIsNodeColumn() { - TableImpl tableNode = new TableImpl(Node.class, false); - TableImpl tableEdge = new TableImpl(Edge.class, false); + TableImpl tableNode = new TableImpl(Node.class); + TableImpl tableEdge = new TableImpl(Edge.class); Column column = tableNode.addColumn("0", Integer.class); Assert.assertTrue(AttributeUtils.isNodeColumn(column)); @@ -861,8 +861,8 @@ public void testIsNodeColumn() { @Test public void testIsEdgeColumn() { - TableImpl tableNode = new TableImpl(Node.class, false); - TableImpl tableEdge = new TableImpl(Edge.class, false); + TableImpl tableNode = new TableImpl(Node.class); + TableImpl tableEdge = new TableImpl(Edge.class); Column column = tableEdge.addColumn("0", Integer.class); Assert.assertTrue(AttributeUtils.isEdgeColumn(column)); diff --git a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java index 46d78721..ebc893aa 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnNoIndexTest.java @@ -244,10 +244,12 @@ private Node addNodeWithAttribute(GraphStore store, Column column, String id, Ob private GraphStore generateGraphStoreWithColumns() { GraphStore graphStore = new GraphStore(); ColumnStore columnStore = graphStore.nodeTable.store; - columnStore.addColumn(new ColumnImpl("foo", String.class, "foo", null, Origin.DATA, false, false)); - columnStore.addColumn(new ColumnImpl("age", Integer.class, "Age", null, Origin.DATA, true, false)); - columnStore - .addColumn(new ColumnImpl("price", TimestampIntegerMap.class, "Price", null, Origin.DATA, true, false)); + columnStore.addColumn(new ColumnImpl(graphStore.nodeTable, "foo", String.class, "foo", null, Origin.DATA, false, + false)); + columnStore.addColumn(new ColumnImpl(graphStore.nodeTable, "age", Integer.class, "Age", null, Origin.DATA, true, + false)); + columnStore.addColumn(new ColumnImpl(graphStore.nodeTable, "price", TimestampIntegerMap.class, "Price", null, + Origin.DATA, true, false)); return graphStore; } diff --git a/src/test/java/org/gephi/graph/impl/ColumnObserverTest.java b/src/test/java/org/gephi/graph/impl/ColumnObserverTest.java index 5595357b..89c12ba3 100644 --- a/src/test/java/org/gephi/graph/impl/ColumnObserverTest.java +++ b/src/test/java/org/gephi/graph/impl/ColumnObserverTest.java @@ -19,6 +19,7 @@ import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnDiff; import org.gephi.graph.api.ColumnObserver; +import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; import org.gephi.graph.api.types.TimestampIntegerMap; @@ -45,6 +46,15 @@ public void testDefaultObserver() { Assert.assertFalse(observer.hasColumnChanged()); } + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testCreateObserverWhenDisabled() { + GraphStore store = new GraphStore(null, Configuration.builder().enableObservers(false).build()); + TableImpl table = store.nodeTable; + Column column = table.addColumn("0", Integer.class); + + column.createColumnObserver(false); + } + @Test(expectedExceptions = RuntimeException.class) public void testGetDiffWhenDisabled() { GraphStore store = new GraphStore(); @@ -255,7 +265,8 @@ public void testSetDynamicAttribute() { @Test public void testDestroyObserver() { - TableImpl table = new TableImpl(Node.class, false); + GraphStore store = new GraphStore(); + TableImpl table = store.nodeTable; Column column = table.addColumn("0", Integer.class); ColumnObserver observer = column.createColumnObserver(false); diff --git a/src/test/java/org/gephi/graph/impl/ConfigurationTest.java b/src/test/java/org/gephi/graph/impl/ConfigurationTest.java index b6e43f4d..1b4296df 100644 --- a/src/test/java/org/gephi/graph/impl/ConfigurationTest.java +++ b/src/test/java/org/gephi/graph/impl/ConfigurationTest.java @@ -25,7 +25,25 @@ public class ConfigurationTest { @Test - public void testDefault() { + public void testDefaultBuilder() { + Configuration c = Configuration.builder().build(); + Assert.assertNotNull(c); + Assert.assertNotNull(c.getNodeIdType()); + Assert.assertEquals(c, Configuration.builder().build()); + Assert.assertEquals(c.hashCode(), Configuration.builder().build().hashCode()); + } + + @Test + public void testBuilderMultipleSet() { + Configuration.Builder b = Configuration.builder(); + Assert.assertEquals(b.build().getNodeIdType(), String.class); + b.nodeIdType(Integer.class); + Assert.assertEquals(b.build().getNodeIdType(), Integer.class); + } + + @Test + @SuppressWarnings("deprecation") + public void testDefaultDeprecated() { Configuration c = new Configuration(); Assert.assertNotNull(c.getNodeIdType()); Assert.assertNotNull(c.getEdgeIdType()); @@ -35,6 +53,13 @@ public void testDefault() { @Test public void testSetNodeIdType() { + Configuration c = Configuration.builder().nodeIdType(Float.class).build(); + Assert.assertEquals(c.getNodeIdType(), Float.class); + } + + @Test + @SuppressWarnings("deprecation") + public void testSetNodeIdTypeDeprecated() { Configuration c = new Configuration(); c.setNodeIdType(Float.class); Assert.assertEquals(c.getNodeIdType(), Float.class); @@ -42,6 +67,13 @@ public void testSetNodeIdType() { @Test public void testSetEdgeIdType() { + Configuration c = Configuration.builder().edgeIdType(Float.class).build(); + Assert.assertEquals(c.getEdgeIdType(), Float.class); + } + + @Test + @SuppressWarnings("deprecation") + public void testSetEdgeIdTypeDeprecated() { Configuration c = new Configuration(); c.setEdgeIdType(Float.class); Assert.assertEquals(c.getEdgeIdType(), Float.class); @@ -49,6 +81,13 @@ public void testSetEdgeIdType() { @Test public void testSetEdgeLabelType() { + Configuration c = Configuration.builder().edgeLabelType(Float.class).build(); + Assert.assertEquals(c.getEdgeLabelType(), Float.class); + } + + @Test + @SuppressWarnings("deprecation") + public void testSetEdgeLabelTypeDeprecated() { Configuration c = new Configuration(); c.setEdgeLabelType(Float.class); Assert.assertEquals(c.getEdgeLabelType(), Float.class); @@ -56,6 +95,17 @@ public void testSetEdgeLabelType() { @Test public void testSetEdgeWeightType() { + Configuration c = Configuration.builder().edgeWeightType(IntervalDoubleMap.class).build(); + Assert.assertEquals(c.getEdgeWeightType(), IntervalDoubleMap.class); + c = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); + Assert.assertEquals(c.getEdgeWeightType(), TimestampDoubleMap.class); + c = Configuration.builder().edgeWeightType(Double.class).build(); + Assert.assertEquals(c.getEdgeWeightType(), Double.class); + } + + @Test + @SuppressWarnings("deprecation") + public void testSetEdgeWeightTypeDeprecated() { Configuration c = new Configuration(); c.setEdgeWeightType(IntervalDoubleMap.class); Assert.assertEquals(c.getEdgeWeightType(), IntervalDoubleMap.class); @@ -67,6 +117,13 @@ public void testSetEdgeWeightType() { @Test public void testSetTimeRepresentation() { + Configuration c = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); + Assert.assertEquals(c.getTimeRepresentation(), TimeRepresentation.INTERVAL); + } + + @Test + @SuppressWarnings("deprecation") + public void testSetTimeRepresentationDeprecated() { Configuration c = new Configuration(); c.setTimeRepresentation(TimeRepresentation.INTERVAL); Assert.assertEquals(c.getTimeRepresentation(), TimeRepresentation.INTERVAL); @@ -74,6 +131,25 @@ public void testSetTimeRepresentation() { @Test public void testSetEdgeWeightColumn() { + Configuration c = Configuration.builder().edgeWeightColumn(false).build(); + Assert.assertEquals(c.getEdgeWeightColumn(), Boolean.FALSE); + } + + @Test + public void testDisableAutoLocking() { + Configuration c = Configuration.builder().enableAutoLocking(false).build(); + Assert.assertEquals(c.isEnableAutoLocking(), Boolean.FALSE); + } + + @Test + public void testDisableTimeIndexing() { + Configuration c = Configuration.builder().enableIndexTime(false).build(); + Assert.assertEquals(c.isEnableIndexTime(), Boolean.FALSE); + } + + @Test + @SuppressWarnings("deprecation") + public void testSetEdgeWeightColumnDeprecated() { Configuration c = new Configuration(); c.setEdgeWeightColumn(Boolean.FALSE); Assert.assertEquals(c.getEdgeWeightColumn(), Boolean.FALSE); @@ -81,61 +157,131 @@ public void testSetEdgeWeightColumn() { @Test(expectedExceptions = IllegalArgumentException.class) public void testSetNodeIdTypeUnsupported() { + Configuration.builder().nodeIdType(int[].class).build(); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testSetNodeIdTypeUnsupportedDeprecated() { Configuration c = new Configuration(); c.setNodeIdType(int[].class); } @Test(expectedExceptions = IllegalArgumentException.class) public void testSetEdgeIdTypeUnsupported() { + Configuration.builder().edgeIdType(int[].class).build(); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + @SuppressWarnings("deprecation") + public void testSetEdgeIdTypeUnsupportedDeprecated() { Configuration c = new Configuration(); c.setEdgeIdType(int[].class); } @Test(expectedExceptions = IllegalArgumentException.class) public void testSetEdgeWeightTypeFloatUnsupported() { + Configuration.builder().edgeWeightType(Float.class).build(); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + @SuppressWarnings("deprecation") + public void testSetEdgeWeightTypeFloatUnsupportedDeprecated() { Configuration c = new Configuration(); c.setEdgeWeightType(Float.class); } @Test(expectedExceptions = IllegalArgumentException.class) public void testSetEdgeWeightTypeNotNumberUnsupported() { + Configuration.builder().edgeWeightType(String.class).build(); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + @SuppressWarnings("deprecation") + public void testSetEdgeWeightTypeNotNumberUnsupportedDeprecated() { Configuration c = new Configuration(); c.setEdgeWeightType(String.class); } @Test - public void testDefaultEquals() { - Assert.assertTrue(new Configuration().equals(new Configuration())); + @SuppressWarnings("deprecation") + public void testDefaultEqualsDeprecated() { + Assert.assertEquals(new Configuration(), new Configuration()); } @Test - public void testDefaultHashCode() { + @SuppressWarnings("deprecation") + public void testDefaultHashCodeDeprecated() { Assert.assertEquals(new Configuration().hashCode(), new Configuration().hashCode()); } @Test public void testEquals() { + Configuration c1 = Configuration.builder().nodeIdType(Float.class).build(); + Configuration c2 = Configuration.builder().build(); + Assert.assertNotEquals(c2, c1); + } + + @Test + @SuppressWarnings("deprecation") + public void testEqualsDeprecated() { Configuration c1 = new Configuration(); Configuration c2 = new Configuration(); + Assert.assertEquals(c2, c1); c2.setNodeIdType(Float.class); - Assert.assertFalse(c1.equals(c2)); + Assert.assertNotEquals(c2, c1); } @Test public void testHashCode() { + Configuration c1 = Configuration.builder().nodeIdType(Float.class).build(); + Configuration c2 = Configuration.builder().build(); + Assert.assertNotEquals(c2.hashCode(), c1.hashCode()); + } + + @Test + @SuppressWarnings("deprecation") + public void testHashCodeDeprecated() { Configuration c1 = new Configuration(); Configuration c2 = new Configuration(); + Assert.assertEquals(c1.hashCode(), c2.hashCode()); c2.setNodeIdType(Float.class); Assert.assertNotEquals(c1.hashCode(), c2.hashCode()); } @Test public void testCopy() { + Configuration c1 = Configuration.builder().build(); + Configuration c2 = c1.copy(); + Assert.assertEquals(c2, c1); + Assert.assertNotSame(c2, c1); + + Configuration c3 = Configuration.builder().nodeIdType(Float.class).build(); + Assert.assertNotEquals(c3, c1); + Configuration c4 = c3.copy(); + Assert.assertEquals(c4, c3); + Assert.assertEquals(Float.class, c4.getNodeIdType()); + } + + @Test + @SuppressWarnings("deprecation") + public void testCopyDeprecated() { Configuration c1 = new Configuration(); Configuration c2 = c1.copy(); - Assert.assertTrue(c1.equals(c2)); + Assert.assertEquals(c2, c1); c1.setNodeIdType(Float.class); Assert.assertNotEquals(c2.getNodeIdType(), Float.class); - Assert.assertFalse(c1.equals(c2)); + Assert.assertNotEquals(c2, c1); + } + + @Test + public void testToConfiguration() { + Configuration c1 = Configuration.builder().nodeIdType(Float.class).build(); + Configuration c2 = new ConfigurationImpl(c1).toConfiguration(); + Assert.assertEquals(c1, c2); + } + + @Test(expectedExceptions = IllegalStateException.class) + public void testExceptionSpatialIndexWithDisabledNodeProperties() { + Configuration.builder().enableSpatialIndex(true).enableNodeProperties(false).build(); } } diff --git a/src/test/java/org/gephi/graph/impl/EdgeImplTest.java b/src/test/java/org/gephi/graph/impl/EdgeImplTest.java index a4405b0d..12d5d483 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeImplTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeImplTest.java @@ -54,8 +54,7 @@ public void testZeroWeight() { @Test public void testGetDefaultTimestampWeight() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(42.0, 1.0); @@ -64,8 +63,7 @@ public void testGetDefaultTimestampWeight() { @Test public void testGetDefaultTimestampWeightWhenNotSet() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); Assert.assertEquals(e.getWeight(2.0), GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING); @@ -73,9 +71,8 @@ public void testGetDefaultTimestampWeightWhenNotSet() { @Test public void testGetDefaultIntervalWeight() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); - config.setEdgeWeightType(IntervalDoubleMap.class); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(42.0, new Interval(1.0, 2.0)); @@ -85,9 +82,8 @@ public void testGetDefaultIntervalWeight() { @Test public void testGetDefaultIntervalWeightWhenNotSet() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); - config.setEdgeWeightType(IntervalDoubleMap.class); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); Assert.assertEquals(e @@ -96,9 +92,8 @@ public void testGetDefaultIntervalWeightWhenNotSet() { @Test public void testGetWeightInterval() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); - config.setEdgeWeightType(IntervalDoubleMap.class); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(42.0, new Interval(1.0, 2.0)); @@ -107,9 +102,8 @@ public void testGetWeightInterval() { @Test public void testGetWeightIntervalMax() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); - config.setEdgeWeightType(IntervalDoubleMap.class); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Column col = graphStore.edgeTable.store.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); @@ -123,8 +117,7 @@ public void testGetWeightIntervalMax() { @Test public void testSetTimestampWeight() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(42.0, 1.0); @@ -136,9 +129,8 @@ public void testSetTimestampWeight() { @Test public void testSetIntervalWeight() { - Configuration config = new Configuration(); - config.setEdgeWeightType(IntervalDoubleMap.class); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); Interval i1 = new Interval(1.0, 2.0); @@ -152,9 +144,8 @@ public void testSetIntervalWeight() { @Test public void testIntervalWeightUsesFirstValueInOverlappingIntervalsEstimator() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); - config.setEdgeWeightType(IntervalDoubleMap.class); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(42.0, new Interval(1.0, 2.0)); @@ -177,8 +168,7 @@ public void testGetWeightIntervalError() { @Test public void testGetWeightStaticError() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(1.0, 1.0); @@ -199,8 +189,7 @@ public void testHasDynamicWeightDouble() { @Test public void testHasDynamicWeightTimestamp() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); Assert.assertTrue(e.hasDynamicWeight()); @@ -210,9 +199,8 @@ public void testHasDynamicWeightTimestamp() { @Test public void testHasDynamicWeightInterval() { - Configuration config = new Configuration(); - config.setEdgeWeightType(IntervalDoubleMap.class); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); Assert.assertTrue(e.hasDynamicWeight()); @@ -222,8 +210,7 @@ public void testHasDynamicWeightInterval() { @Test public void testGetDefaultWeightByGraphView() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); Assert.assertEquals(e @@ -232,8 +219,7 @@ public void testGetDefaultWeightByGraphView() { @Test public void testGetTimestampWeightMainGraphView() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(42.0, 1.0); @@ -244,9 +230,8 @@ public void testGetTimestampWeightMainGraphView() { @Test public void testGetWeightGraphViewMax() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); - config.setEdgeWeightType(IntervalDoubleMap.class); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Column col = graphStore.edgeTable.store.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); @@ -262,8 +247,7 @@ public void testGetWeightGraphViewMax() { @Test public void testGetWeightNoValue() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setAttribute("weight", null); @@ -273,8 +257,7 @@ public void testGetWeightNoValue() { @Test public void testGetWeightDefaultEstimator() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(10.0, 1.0); @@ -284,8 +267,7 @@ public void testGetWeightDefaultEstimator() { @Test public void testGetWeightAverageEstimator() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); Column col = graphStore.edgeTable.store.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); @@ -297,8 +279,7 @@ public void testGetWeightAverageEstimator() { @Test public void testGetWeightWithView() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(10.0, 1.0); @@ -312,8 +293,7 @@ public void testGetWeightWithView() { @Test public void testGetWeightWithViewStatic() { - Configuration config = new Configuration(); - config.setEdgeWeightType(Double.class); + Configuration config = Configuration.builder().edgeWeightType(Double.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(10.0); @@ -324,8 +304,7 @@ public void testGetWeightWithViewStatic() { @Test public void testGetWeightsTimestamp() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(10.0, 3.0); @@ -346,9 +325,8 @@ public void testGetWeightsTimestamp() { @Test public void testGetWeightsInterval() { - Configuration config = new Configuration(); - config.setEdgeWeightType(IntervalDoubleMap.class); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); e.setWeight(10.0, new Interval(3.0, 4.0)); @@ -377,8 +355,7 @@ public void testGetWeightsStatic() { @Test public void testSetAttributeWeightTimestamp() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore graphStore = GraphGenerator.generateTinyGraphStore(config); Edge e = graphStore.getEdge("0"); Column col = graphStore.edgeTable.getColumn(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); @@ -402,7 +379,7 @@ public void testGetTypeLabelDefault() { @Test public void testGetTypeLabelCustom() { - GraphStore graphStore = new GraphStore(null); + GraphStore graphStore = new GraphStore(); EdgeTypeStore edgeTypeStore = graphStore.edgeTypeStore; int typeId = edgeTypeStore.addType("foo"); @@ -449,4 +426,21 @@ public void testGetTable() { Assert.assertSame(e.getTable(), graphStore.getModel().getEdgeTable()); } } + + @Test + public void testProperties() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + Edge e = graphStore.getEdge("0"); + Assert.assertNotNull(e.getTextProperties()); + Assert.assertNotNull(e.getColor()); + Assert.assertEquals(e.alpha(), 1f); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testPropertiesDisabled() { + GraphStore graphStore = GraphGenerator + .generateTinyGraphStore(Configuration.builder().enableEdgeProperties(false).build()); + Edge e = graphStore.getEdge("0"); + Assert.assertNull(e.getColor()); + } } diff --git a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java index c6950055..71e1b8ae 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Random; import java.util.Set; +import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.testng.Assert; import org.testng.annotations.Test; @@ -1302,7 +1303,7 @@ public void testMutualParallel() { public void testRemoveMutualEdge() { EdgeImpl[] edges = GraphGenerator.generateMutualEdges(1); EdgeTypeStore edgeTypeStore = new EdgeTypeStore(); - EdgeStore edgeStore = new EdgeStore(edgeTypeStore, null, null, null, null); + EdgeStore edgeStore = new EdgeStore(edgeTypeStore, null, null, null, null, null); edgeStore.addAll(Arrays.asList(edges)); edgeStore.remove(edges[0]); Assert.assertFalse(edges[0].isMutual()); @@ -1652,7 +1653,7 @@ public void testEdgeIncident() { @Test public void testTypeCounting() { EdgeTypeStore edgeTypeStore = new EdgeTypeStore(); - EdgeStore edgeStore = new EdgeStore(edgeTypeStore, null, null, null, null); + EdgeStore edgeStore = new EdgeStore(edgeTypeStore, null, null, null, null, null); EdgeImpl[] edges = GraphGenerator.generateSmallMultiTypeEdgeList(); Int2IntMap counts = new Int2IntOpenHashMap(); @@ -1898,7 +1899,7 @@ public void testSetTypeBeforeAdd() { @Test public void testSetType() { - EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(), null, null, null, null); + EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(), null, null, null, null, null); EdgeImpl edge = GraphGenerator.generateSingleEdge(4); edgeStore.add(edge); edgeStore.setEdgeType(edge, 1); @@ -1910,9 +1911,28 @@ public void testSetType() { Assert.assertEquals(1, edgeStore.size(1)); } + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testAddWithoutAutoRegistration() { + ConfigurationImpl config = new ConfigurationImpl( + Configuration.builder().enableAutoEdgeTypeRegistration(false).build()); + EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(config), null, config, null, null, null); + EdgeImpl edge = GraphGenerator.generateSingleEdge(4); + edgeStore.add(edge); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testSetTypeWithoutAutoRegistration() { + ConfigurationImpl config = new ConfigurationImpl( + Configuration.builder().enableAutoEdgeTypeRegistration(false).build()); + EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(config), null, config, null, null, null); + EdgeImpl edge = GraphGenerator.generateSingleEdge(); + edgeStore.add(edge); + edgeStore.setEdgeType(edge, 1); + } + @Test public void testSetTypeWithMutualEdge() { - EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(), null, null, null, null); + EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(), null, null, null, null, null); EdgeImpl[] edges = GraphGenerator.generateMutualEdges(4); edgeStore.addAll(Arrays.asList(edges)); Assert.assertTrue(edges[0].isMutual()); @@ -1922,10 +1942,11 @@ public void testSetTypeWithMutualEdge() { Assert.assertFalse(edges[1].isMutual()); } - // Can only run when GraphStoreConfiguration.ENABLE_PARALLEL_EDGES = false - @Test(enabled = false) + @Test public void testReturnFalseWithoutParallelEdges() { - EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(), null, null, null, null); + ConfigurationImpl configuration = new ConfigurationImpl( + Configuration.builder().enableParallelEdgesSameType(false).build()); + EdgeStore edgeStore = new EdgeStore(new EdgeTypeStore(), null, configuration, null, null, null); EdgeImpl edge = GraphGenerator.generateSingleEdge(1); EdgeImpl edge2 = new EdgeImpl('0', edge.graphStore, edge.source, edge.target, 2, 1.0, true); Assert.assertTrue(edgeStore.add(edge)); diff --git a/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java index 4bde021a..b531e34a 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeTypeStoreTest.java @@ -15,6 +15,7 @@ */ package org.gephi.graph.impl; +import org.gephi.graph.api.Configuration; import org.testng.Assert; import org.testng.annotations.Test; @@ -283,8 +284,8 @@ public void testAddDirectTypeNoGarbage() { @Test public void testAddDifferentType() { - EdgeTypeStore edgeTypeStore = new EdgeTypeStore(); - edgeTypeStore.configuration.setEdgeLabelType(Integer.class); + EdgeTypeStore edgeTypeStore = new EdgeTypeStore( + new ConfigurationImpl(Configuration.builder().edgeLabelType(Integer.class).build())); int id = edgeTypeStore.addType(42); Assert.assertEquals(id, 1); Assert.assertEquals(edgeTypeStore.getLabel(1), 42); diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index e4467c74..2b37108d 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -106,9 +106,10 @@ public void testSetAttributeString() { @Test public void testSetAttributeStandardizedType() { GraphStore store = new GraphStore(); - store.nodeTable.store - .addColumn(new ColumnImpl("arr1", Integer[].class, "Array", null, Origin.DATA, true, false)); - store.nodeTable.store.addColumn(new ColumnImpl("arr2", int[].class, "Array", null, Origin.DATA, true, false)); + store.nodeTable.store.addColumn(new ColumnImpl(store.nodeTable, "arr1", Integer[].class, "Array", null, + Origin.DATA, true, false)); + store.nodeTable.store.addColumn(new ColumnImpl(store.nodeTable, "arr2", int[].class, "Array", null, Origin.DATA, + true, false)); Column column1 = store.nodeTable.store.getColumn("arr1"); Column column2 = store.nodeTable.store.getColumn("arr2"); @@ -588,7 +589,8 @@ public void testGetAttributeNonTimestamp() { public void testGetDefaultValue() { GraphStore store = new GraphStore(); Integer defaultValue = 25; - Column column = new ColumnImpl("age", Integer.class, "Age", defaultValue, Origin.DATA, true, false); + Column column = new ColumnImpl(store.nodeTable, "age", Integer.class, "Age", defaultValue, Origin.DATA, true, + false); store.nodeTable.store.addColumn(column); NodeImpl node = new NodeImpl("0", store); @@ -1195,8 +1197,7 @@ public void testEnsureCapacity() { // Utility private GraphStore getIntervalGraphStore() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); GraphStore store = graphModel.store; return store; diff --git a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java index ab5017dd..fabdd91f 100644 --- a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java @@ -36,8 +36,16 @@ public class GraphBridgeTest { @Test(expectedExceptions = RuntimeException.class) public void testVerifyConfiguration() { - Configuration destConfig = new Configuration(); - destConfig.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration destConfig = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); + GraphModelImpl dest = new GraphModelImpl(destConfig); + + new GraphBridgeImpl(dest.store).copyNodes(GraphGenerator.generateTinyGraphStore().getNodes().toArray()); + } + + @Test(expectedExceptions = RuntimeException.class) + public void testVerifyConfigurationParallelEdges() { + Configuration destConfig = Configuration.builder() + .enableParallelEdgesSameType(!GraphStoreConfiguration.DEFAULT_ENABLE_PARALLEL_EDGES_SAME_TYPE).build(); GraphModelImpl dest = new GraphModelImpl(destConfig); new GraphBridgeImpl(dest.store).copyNodes(GraphGenerator.generateTinyGraphStore().getNodes().toArray()); @@ -182,8 +190,7 @@ public void testCopyEdgeWeightStatic() { @Test public void testCopyEdgeWeightTimestamp() { - Configuration config = new Configuration(); - config.setEdgeWeightType(TimestampDoubleMap.class); + Configuration config = Configuration.builder().edgeWeightType(TimestampDoubleMap.class).build(); GraphStore source = GraphGenerator.generateTinyGraphStore(config); EdgeImpl e0 = source.getEdge("0"); e0.setWeight(42.0, 1.0); @@ -200,9 +207,8 @@ public void testCopyEdgeWeightTimestamp() { @Test public void testCopyEdgeWeightInterval() { - Configuration config = new Configuration(); - config.setEdgeWeightType(IntervalDoubleMap.class); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(IntervalDoubleMap.class).build(); GraphStore source = GraphGenerator.generateTinyGraphStore(config); EdgeImpl e0 = source.getEdge("0"); e0.setWeight(42.0, new Interval(1.0, 2.0)); @@ -300,8 +306,7 @@ public void testCopyNodeAttributesInterval() { n1.setAttribute(c2, 10, new Interval(1.0, 2.0)); n1.setAttribute(c2, 20, new Interval(3.0, 4.0)); - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl model = new GraphModelImpl(config); GraphStore dest = model.store; new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); @@ -353,8 +358,7 @@ public void testCopyIntervalSet() { Node n1 = source.getNode("1"); n1.addInterval(new Interval(1.0, 2.0)); - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl model = new GraphModelImpl(config); GraphStore dest = model.store; new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); diff --git a/src/test/java/org/gephi/graph/impl/GraphFactoryTest.java b/src/test/java/org/gephi/graph/impl/GraphFactoryTest.java index d82a992c..ff5bb469 100644 --- a/src/test/java/org/gephi/graph/impl/GraphFactoryTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphFactoryTest.java @@ -132,8 +132,7 @@ public void testNewEdgeWithId() { @Test public void testIntegerNodeId() { - Configuration config = new Configuration(); - config.setNodeIdType(Integer.class); + Configuration config = Configuration.builder().nodeIdType(Integer.class).build(); GraphModelImpl graphModel = new GraphModelImpl(config); GraphFactoryImpl graphFactory = new GraphFactoryImpl(graphModel.store); Node node = graphFactory.newNode(); @@ -142,8 +141,7 @@ public void testIntegerNodeId() { @Test public void testIntegerEdgeId() { - Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); + Configuration config = Configuration.builder().edgeIdType(Integer.class).build(); GraphModelImpl graphModel = new GraphModelImpl(config); GraphFactoryImpl graphFactory = new GraphFactoryImpl(graphModel.store); @@ -156,8 +154,7 @@ public void testIntegerEdgeId() { @Test(expectedExceptions = UnsupportedOperationException.class) public void testUnsupportedNodeId() { - Configuration config = new Configuration(); - config.setNodeIdType(Float.class); + Configuration config = Configuration.builder().nodeIdType(Float.class).build(); GraphModelImpl graphModel = new GraphModelImpl(config); GraphFactoryImpl graphFactory = new GraphFactoryImpl(graphModel.store); graphFactory.newNode(); @@ -165,8 +162,7 @@ public void testUnsupportedNodeId() { @Test(expectedExceptions = UnsupportedOperationException.class) public void testUnsupportedEdgeId() { - Configuration config = new Configuration(); - config.setEdgeIdType(Float.class); + Configuration config = Configuration.builder().edgeIdType(Float.class).build(); GraphModelImpl graphModel = new GraphModelImpl(config); GraphFactoryImpl graphFactory = new GraphFactoryImpl(graphModel.store); @@ -178,8 +174,7 @@ public void testUnsupportedEdgeId() { @Test public void testAutoIncrementNodeInt() { - Configuration config = new Configuration(); - config.setNodeIdType(Integer.class); + Configuration config = Configuration.builder().nodeIdType(Integer.class).build(); GraphModelImpl graphModel = new GraphModelImpl(config); GraphFactoryImpl graphFactory = new GraphFactoryImpl(graphModel.store); graphFactory.newNode(10); @@ -192,8 +187,7 @@ public void testAutoIncrementNodeInt() { @Test public void testAutoIncrementEdgeInt() { - Configuration config = new Configuration(); - config.setEdgeIdType(Integer.class); + Configuration config = Configuration.builder().edgeIdType(Integer.class).build(); GraphModelImpl graphModel = new GraphModelImpl(config); GraphFactoryImpl graphFactory = new GraphFactoryImpl(graphModel.store); Node n1 = graphFactory.newNode(); @@ -210,7 +204,7 @@ public void testAutoIncrementEdgeInt() { @Test public void testAutoIncrementNodeString() { - GraphModelImpl graphModel = new GraphModelImpl(new Configuration()); + GraphModelImpl graphModel = new GraphModelImpl(); GraphFactoryImpl graphFactory = new GraphFactoryImpl(graphModel.store); graphFactory.newNode("10"); Assert.assertEquals(graphFactory.newNode().getId(), "11"); @@ -224,7 +218,7 @@ public void testAutoIncrementNodeString() { @Test public void testAutoIncrementNotInteger() { - GraphModelImpl graphModel = new GraphModelImpl(new Configuration()); + GraphModelImpl graphModel = new GraphModelImpl(); GraphFactoryImpl graphFactory = new GraphFactoryImpl(graphModel.store); graphFactory.newNode("3543543523242"); Assert.assertEquals(graphFactory.newNode().getId(), "0"); @@ -232,7 +226,7 @@ public void testAutoIncrementNotInteger() { @Test public void testAutoIncrementEdgeString() { - GraphModelImpl graphModel = new GraphModelImpl(new Configuration()); + GraphModelImpl graphModel = new GraphModelImpl(); GraphFactoryImpl graphFactory = new GraphFactoryImpl(graphModel.store); Node n1 = graphFactory.newNode(); Node n2 = graphFactory.newNode(); diff --git a/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/src/test/java/org/gephi/graph/impl/GraphGenerator.java index e81601c1..d9a351b0 100644 --- a/src/test/java/org/gephi/graph/impl/GraphGenerator.java +++ b/src/test/java/org/gephi/graph/impl/GraphGenerator.java @@ -413,7 +413,7 @@ public static GraphStore generateEmptyGraphStore() { } public static GraphStore generateTinyUndirectedGraphStore() { - GraphModelImpl graphModel = new GraphModelImpl(new Configuration()); + GraphModelImpl graphModel = new GraphModelImpl(Configuration.builder().build()); GraphStore graphStore = graphModel.store; Node n1 = graphStore.factory.newNode("1"); Node n2 = graphStore.factory.newNode("2"); @@ -441,19 +441,17 @@ public static GraphStore generateEmptyGraphStore(Configuration configuration) { } public static GraphStore generateEmptyGraphStore(TimeRepresentation timeRepresentation) { - Configuration config = new Configuration(); - config.setTimeRepresentation(timeRepresentation); + Configuration config = Configuration.builder().timeRepresentation(timeRepresentation).build(); return generateEmptyGraphStore(config); } public static GraphStore generateTinyGraphStore(TimeRepresentation timeRepresentation) { - Configuration config = new Configuration(); - config.setTimeRepresentation(timeRepresentation); + Configuration config = Configuration.builder().timeRepresentation(timeRepresentation).build(); return generateTinyGraphStore(config); } - public static GraphStore generateTinyGraphStoreWithSelfLoop() { - GraphModelImpl graphModel = new GraphModelImpl(new Configuration()); + public static GraphStore generateTinyGraphStoreWithSelfLoop(Configuration configuration) { + GraphModelImpl graphModel = new GraphModelImpl(configuration); GraphStore graphStore = graphModel.store; NodeImpl n1 = new NodeImpl("1", graphStore); EdgeImpl e = new EdgeImpl("0", graphStore, n1, n1, EdgeTypeStore.NULL_LABEL, 1.0, true); @@ -462,8 +460,12 @@ public static GraphStore generateTinyGraphStoreWithSelfLoop() { return graphStore; } + public static GraphStore generateTinyGraphStoreWithSelfLoop() { + return generateTinyGraphStoreWithSelfLoop(Configuration.builder().build()); + } + public static GraphStore generateTinyGraphStoreWithMutualEdge() { - GraphModelImpl graphModel = new GraphModelImpl(new Configuration()); + GraphModelImpl graphModel = new GraphModelImpl(); GraphStore graphStore = graphModel.store; NodeImpl n1 = new NodeImpl("1", graphStore); NodeImpl n2 = new NodeImpl("2", graphStore); diff --git a/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/src/test/java/org/gephi/graph/impl/GraphModelTest.java index 7eb3c45d..3fb68e4a 100644 --- a/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -344,6 +344,14 @@ public void testGetNodeIndex() { Index index = graphModel.getNodeIndex(); Assert.assertEquals(index.count(col, "bar"), 1); + Assert.assertSame(graphModel.getElementIndex(table), index); + } + + @Test + public void testGetNodeIndexWithIndexConfigDisabled() { + GraphModelImpl graphModel = new GraphModelImpl(Configuration.builder().enableIndexNodes(false).build()); + Assert.assertNotNull(graphModel.getNodeIndex()); + Assert.assertNotNull(graphModel.getNodeIndex().getColumnIndex(graphModel.defaultColumns().nodeId())); } @Test @@ -360,6 +368,7 @@ public void testGetNodeIndexInView() { Index index = graphModel.getNodeIndex(view); Assert.assertEquals(index.count(col, "bar"), 1); + Assert.assertSame(graphModel.getElementIndex(table, view), index); } @Test @@ -376,6 +385,14 @@ public void testGetEdgeIndex() { Index index = graphModel.getEdgeIndex(); Assert.assertEquals(index.count(col, "bar"), 1); + Assert.assertSame(graphModel.getElementIndex(table), index); + } + + @Test + public void testGetEdgeIndexWithIndexConfigDisabled() { + GraphModelImpl graphModel = new GraphModelImpl(Configuration.builder().enableIndexEdges(false).build()); + Assert.assertNotNull(graphModel.getEdgeIndex()); + Assert.assertNotNull(graphModel.getEdgeIndex().getColumnIndex(graphModel.defaultColumns().edgeId())); } @Test @@ -395,6 +412,7 @@ public void testGetEdgeIndexInView() { Index index = graphModel.getEdgeIndex(view); Assert.assertEquals(index.count(col, "bar"), 1); + Assert.assertSame(graphModel.getElementIndex(table, view), index); } @Test @@ -409,6 +427,12 @@ public void testGetNodeTimestampIndex() { Assert.assertEquals(index.getMaxTimestamp(), 1.0); } + @Test + public void testGetNodeTimeIndexWithIndexConfigDisabled() { + GraphModelImpl graphModel = new GraphModelImpl(Configuration.builder().enableIndexTime(false).build()); + Assert.assertNull(graphModel.getNodeTimeIndex()); + } + @Test public void testGetEdgeTimestampIndex() { GraphModelImpl graphModel = new GraphModelImpl(); @@ -423,6 +447,12 @@ public void testGetEdgeTimestampIndex() { Assert.assertEquals(index.getMaxTimestamp(), 1.0); } + @Test + public void testGetEdgeTimeIndexWithIndexConfigDisabled() { + GraphModelImpl graphModel = new GraphModelImpl(Configuration.builder().enableIndexTime(false).build()); + Assert.assertNull(graphModel.getEdgeTimeIndex()); + } + @Test public void testGetNodeTimestampIndexInView() { GraphModelImpl graphModel = new GraphModelImpl(); @@ -478,16 +508,15 @@ public void testSerializationReadWithoutVersionHeader() throws IOException { @Test public void testGetConfiguration() { - Configuration config = new Configuration(); - config.setNodeIdType(Long.class); + Configuration config = Configuration.builder().nodeIdType(Long.class).build(); GraphModelImpl graphModelImpl = new GraphModelImpl(config); Assert.assertEquals(graphModelImpl.getConfiguration(), config); } @Test + @SuppressWarnings("deprecated") public void testGetConfigurationCopy() { - Configuration config = new Configuration(); - config.setNodeIdType(Long.class); + Configuration config = Configuration.builder().nodeIdType(Long.class).build(); GraphModelImpl graphModelImpl = new GraphModelImpl(config); Assert.assertEquals(graphModelImpl.getConfiguration(), config); config.setNodeIdType(Float.class); @@ -496,12 +525,9 @@ public void testGetConfigurationCopy() { @Test public void testSetConfigurationIntervals() { - Configuration config = new Configuration(); + Configuration config = Configuration.builder().nodeIdType(Integer.class).edgeIdType(Byte.class) + .timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModelImpl = new GraphModelImpl(config); - config.setNodeIdType(Integer.class); - config.setEdgeIdType(Byte.class); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); - graphModelImpl.setConfiguration(config); Assert.assertEquals(graphModelImpl.getConfiguration(), config); Assert.assertEquals(graphModelImpl.getNodeTable().getColumn("id").getTypeClass(), Integer.class); Assert.assertEquals(graphModelImpl.getEdgeTable().getColumn("id").getTypeClass(), Byte.class); @@ -516,20 +542,13 @@ public void testSetConfigurationIntervals() { Assert.assertEquals(graphModelImpl.getEdgeTable().getColumn(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) .getTypeClass(), Double.class); - config.setEdgeWeightType(IntervalDoubleMap.class); - graphModelImpl.setConfiguration(config); - Assert.assertEquals(graphModelImpl.getEdgeTable().getColumn(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) - .getTypeClass(), IntervalDoubleMap.class); } @Test public void testSetConfigurationTimestamps() { - Configuration config = new Configuration(); + Configuration config = Configuration.builder().nodeIdType(Integer.class).edgeIdType(Byte.class) + .timeRepresentation(TimeRepresentation.TIMESTAMP).build(); GraphModelImpl graphModelImpl = new GraphModelImpl(config); - config.setNodeIdType(Integer.class); - config.setEdgeIdType(Byte.class); - config.setTimeRepresentation(TimeRepresentation.TIMESTAMP); - graphModelImpl.setConfiguration(config); Assert.assertEquals(graphModelImpl.getConfiguration(), config); Assert.assertEquals(graphModelImpl.getNodeTable().getColumn("id").getTypeClass(), Integer.class); Assert.assertEquals(graphModelImpl.getEdgeTable().getColumn("id").getTypeClass(), Byte.class); @@ -544,72 +563,33 @@ public void testSetConfigurationTimestamps() { Assert.assertEquals(graphModelImpl.getEdgeTable().getColumn(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) .getTypeClass(), Double.class); - config.setEdgeWeightType(TimestampDoubleMap.class); - graphModelImpl.setConfiguration(config); - Assert.assertEquals(graphModelImpl.getEdgeTable().getColumn(GraphStoreConfiguration.EDGE_WEIGHT_INDEX) - .getTypeClass(), TimestampDoubleMap.class); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadEdgeWeightTypeConfigurationIntervals() { - Configuration config = new Configuration(); - GraphModelImpl graphModelImpl = new GraphModelImpl(config); - config.setEdgeWeightType(TimestampDoubleMap.class); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); - graphModelImpl.setConfiguration(config); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL) + .edgeWeightType(TimestampDoubleMap.class).build(); + new GraphModelImpl(config); } @Test(expectedExceptions = IllegalArgumentException.class) public void testBadEdgeWeightTypeConfigurationTimestamps() { - Configuration config = new Configuration(); - GraphModelImpl graphModelImpl = new GraphModelImpl(config); - config.setEdgeWeightType(IntervalDoubleMap.class); - config.setTimeRepresentation(TimeRepresentation.TIMESTAMP); - graphModelImpl.setConfiguration(config); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testSetConfigurationWithNodes() { - GraphModelImpl graphModelImpl = new GraphModelImpl(); - graphModelImpl.store.addNode(graphModelImpl.factory().newNode()); - graphModelImpl.setConfiguration(new Configuration()); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testSetConfigurationWithGraphAttributes() { - GraphModelImpl graphModelImpl = new GraphModelImpl(); - graphModelImpl.getGraph().setAttribute("foo", "bar"); - graphModelImpl.setConfiguration(new Configuration()); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testSetConfigurationWithNodeColumns() { - GraphModelImpl graphModelImpl = new GraphModelImpl(); - graphModelImpl.store.nodeTable.addColumn("foo", Integer.class); - graphModelImpl.setConfiguration(new Configuration()); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.TIMESTAMP) + .edgeWeightType(IntervalDoubleMap.class).build(); + new GraphModelImpl(config); } - @Test(expectedExceptions = IllegalStateException.class) - public void testSetConfigurationWithEdgeColumns() { + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testSetConfiguration() { GraphModelImpl graphModelImpl = new GraphModelImpl(); - graphModelImpl.store.edgeTable.addColumn("foo", Integer.class); - graphModelImpl.setConfiguration(new Configuration()); - } - - @Test(expectedExceptions = IllegalStateException.class) - public void testSetConfigurationWithEdgeType() { - GraphModelImpl graphModelImpl = new GraphModelImpl(); - graphModelImpl.store.edgeTypeStore.addType("foo"); - graphModelImpl.setConfiguration(new Configuration()); + graphModelImpl.setConfiguration(Configuration.builder().build()); } @Test public void testSetConfigurationEdgeWeightColumnFalse() { - GraphModelImpl graphModelImpl = new GraphModelImpl(); + Configuration config = Configuration.builder().edgeWeightColumn(false).build(); + GraphModelImpl graphModelImpl = new GraphModelImpl(config); - Configuration config = new Configuration(); - config.setEdgeWeightColumn(Boolean.FALSE); - graphModelImpl.setConfiguration(config); Assert.assertFalse(graphModelImpl.store.edgeTable.hasColumn("weight")); Assert.assertNotEquals(graphModelImpl.store.edgeTable.addColumn("foo", Integer.class) .getIndex(), GraphStoreConfiguration.EDGE_WEIGHT_INDEX); @@ -617,40 +597,14 @@ public void testSetConfigurationEdgeWeightColumnFalse() { @Test public void testSetConfigurationEdgeWeightColumnTrue() { - Configuration config = new Configuration(); - config.setEdgeWeightColumn(Boolean.FALSE); + Configuration config = Configuration.builder().edgeWeightColumn(true).build(); GraphModelImpl graphModelImpl = new GraphModelImpl(config); - config = new Configuration(); - config.setEdgeWeightColumn(Boolean.TRUE); - graphModelImpl.setConfiguration(config); Assert.assertTrue(graphModelImpl.store.edgeTable.hasColumn("weight")); Assert.assertEquals(graphModelImpl.store.edgeTable.getColumn("weight") .getIndex(), GraphStoreConfiguration.EDGE_WEIGHT_INDEX); } - @Test - public void testSetConfigurationDefaultColumns() { - Configuration config = new Configuration(); - GraphModelImpl graphModelImpl = new GraphModelImpl(config); - - Configuration newConfig = new Configuration(); - newConfig.setNodeIdType(Integer.class); - newConfig.setEdgeIdType(Integer.class); - newConfig.setTimeRepresentation(TimeRepresentation.TIMESTAMP); - newConfig.setEdgeWeightType(TimestampDoubleMap.class); - graphModelImpl.setConfiguration(newConfig); - - Assert.assertSame(graphModelImpl.defaultColumns().nodeId(), graphModelImpl.getNodeTable().getColumn("id")); - Assert.assertSame(graphModelImpl.defaultColumns().edgeId(), graphModelImpl.getEdgeTable().getColumn("id")); - Assert.assertSame(graphModelImpl.defaultColumns().nodeTimeSet(), graphModelImpl.getNodeTable() - .getColumn("timeset")); - Assert.assertSame(graphModelImpl.defaultColumns().edgeTimeSet(), graphModelImpl.getEdgeTable() - .getColumn("timeset")); - Assert.assertSame(graphModelImpl.defaultColumns().edgeWeight(), graphModelImpl.getEdgeTable() - .getColumn("weight")); - } - @Test public void testNodeAttributesAddAndRemoveColumns1() { GraphModelImpl graphModel = new GraphModelImpl(); @@ -688,34 +642,6 @@ public void testNodeAttributesAddAndRemoveColumns2() { n1.setAttribute(col2, "test"); } - @Test - public void testReplaceEdgeWeightColumnUpdatesConfiguration() { - GraphModelImpl graphModel = new GraphModelImpl(); - Graph graph = graphModel.getGraph(); - - Table table = graphModel.getEdgeTable(); - - Node n1 = graphModel.factory().newNode("1"); - Node n2 = graphModel.factory().newNode("2"); - Edge edge = graphModel.factory().newEdge(n1, n2); - graph.addNode(n1); - graph.addNode(n2); - graph.addEdge(edge); - - Assert.assertTrue(graphModel.getConfiguration().getEdgeWeightColumn()); - Assert.assertEquals(graphModel.getConfiguration().getEdgeWeightType(), Double.class); - Assert.assertFalse(edge.hasDynamicWeight()); - - table.removeColumn(GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID); - Assert.assertFalse(graphModel.getConfiguration().getEdgeWeightColumn()); - - table.addColumn(GraphStoreConfiguration.EDGE_WEIGHT_COLUMN_ID, IntervalDoubleMap.class, Origin.PROPERTY); - - Assert.assertTrue(graphModel.getConfiguration().getEdgeWeightColumn()); - Assert.assertEquals(graphModel.getConfiguration().getEdgeWeightType(), IntervalDoubleMap.class); - Assert.assertTrue(edge.hasDynamicWeight()); - } - @Test public void testRemoveColumnWithNodes() { GraphModelImpl graphModel = new GraphModelImpl(); diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index f732630c..5caf9d2d 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -473,7 +473,7 @@ public void testAddEdge() { NodeImpl[] nodes = GraphGenerator.generateNodeList(2); graphStore.addAllNodes(Arrays.asList(nodes)); - EdgeImpl edge = new EdgeImpl("0", nodes[0], nodes[1], 0, 1.0, true); + EdgeImpl edge = new EdgeImpl("0", graphStore, nodes[0], nodes[1], 0, 1.0, true); boolean a = graphStore.addEdge(edge); boolean b = graphStore.addEdge(edge); @@ -491,8 +491,8 @@ public void testAddEdgeWithSameType() { NodeImpl[] nodes = GraphGenerator.generateNodeList(2); graphStore.addAllNodes(Arrays.asList(nodes)); - EdgeImpl edge1 = new EdgeImpl("0", nodes[0], nodes[1], 0, 1.0, true); - EdgeImpl edge2 = new EdgeImpl("1", nodes[0], nodes[1], 0, 1.0, true); + EdgeImpl edge1 = new EdgeImpl("0", graphStore, nodes[0], nodes[1], 0, 1.0, true); + EdgeImpl edge2 = new EdgeImpl("1", graphStore, nodes[0], nodes[1], 0, 1.0, true); boolean a = graphStore.addEdge(edge1); boolean b = graphStore.addEdge(edge2); @@ -512,7 +512,7 @@ public void testAddEdgeTypeRegistration() { EdgeTypeStore typeStore = graphStore.edgeTypeStore; Assert.assertFalse(typeStore.contains(1)); - EdgeImpl edge = new EdgeImpl("0", nodes[0], nodes[1], 1, 1.0, true); + EdgeImpl edge = new EdgeImpl("0", graphStore, nodes[0], nodes[1], 1, 1.0, true); graphStore.addEdge(edge); Assert.assertTrue(typeStore.contains(1)); @@ -525,7 +525,7 @@ public void testAddAllEdges() { NodeImpl[] nodes = GraphGenerator.generateNodeList(2); graphStore.addAllNodes(Arrays.asList(nodes)); - EdgeImpl edge = new EdgeImpl("0", nodes[0], nodes[1], 0, 1.0, true); + EdgeImpl edge = new EdgeImpl("0", graphStore, nodes[0], nodes[1], 0, 1.0, true); graphStore.addAllEdges(Collections.singletonList(edge)); Assert.assertTrue(graphStore.contains(edge)); @@ -537,7 +537,7 @@ public void testAddAllEdgesTypeRegistration() { NodeImpl[] nodes = GraphGenerator.generateNodeList(2); graphStore.addAllNodes(Arrays.asList(nodes)); - EdgeImpl edge = new EdgeImpl("0", nodes[0], nodes[1], 1, 1.0, true); + EdgeImpl edge = new EdgeImpl("0", graphStore, nodes[0], nodes[1], 1, 1.0, true); graphStore.addAllEdges(Collections.singletonList(edge)); Assert.assertTrue(graphStore.edgeTypeStore.contains(1)); diff --git a/src/test/java/org/gephi/graph/impl/IndexImplTest.java b/src/test/java/org/gephi/graph/impl/IndexImplTest.java index 13babeb9..91fc637b 100644 --- a/src/test/java/org/gephi/graph/impl/IndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexImplTest.java @@ -19,6 +19,7 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; +import org.gephi.graph.api.Table; import org.testng.Assert; import org.testng.annotations.Test; @@ -26,16 +27,16 @@ public class IndexImplTest { @Test public void testIndexName() { - ColumnStore columnStore = generateEmptyNodeStore(); - IndexImpl index = columnStore.indexStore.mainIndex; + TableImpl nodeTable = generateEmptyNodeTable(); + IndexImpl index = nodeTable.store.indexStore.mainIndex; Assert.assertEquals(index.getIndexClass(), Node.class); Assert.assertEquals(index.getIndexName(), "index_" + Node.class.getCanonicalName()); } @Test public void testAddColumn() { - ColumnStore columnStore = generateEmptyNodeStore(); - IndexImpl index = columnStore.indexStore.mainIndex; + TableImpl nodeTable = generateEmptyNodeTable(); + IndexImpl index = nodeTable.store.indexStore.mainIndex; ColumnImpl col = new ColumnImpl("foo", String.class, "foo", null, Origin.DATA, true, false); col.setStoreId(0); @@ -47,8 +48,8 @@ public void testAddColumn() { @Test public void testHasColumn() { - ColumnStore columnStore = generateEmptyNodeStore(); - IndexImpl index = columnStore.indexStore.mainIndex; + TableImpl nodeTable = generateEmptyNodeTable(); + IndexImpl index = nodeTable.store.indexStore.mainIndex; ColumnImpl col1 = new ColumnImpl("foo", String.class, "foo", null, Origin.DATA, true, false); ColumnImpl col2 = new ColumnImpl("bar", String.class, "bar", null, Origin.DATA, false, false); col1.setStoreId(0); @@ -63,11 +64,11 @@ public void testHasColumn() { @Test public void testHasColumnDifferentIndex() { - ColumnStore columnStore1 = generateEmptyNodeStore(); - IndexImpl index1 = columnStore1.indexStore.mainIndex; + TableImpl nodeTable = generateEmptyNodeTable(); + IndexImpl index1 = nodeTable.store.indexStore.mainIndex; - ColumnStore columnStore2 = generateEmptyNodeStore(); - IndexImpl index2 = columnStore2.indexStore.mainIndex; + TableImpl nodeTable2 = generateEmptyNodeTable(); + IndexImpl index2 = nodeTable2.store.indexStore.mainIndex; ColumnImpl col1 = new ColumnImpl("foo", String.class, "foo", null, Origin.DATA, true, false); ColumnImpl col2 = new ColumnImpl("bar", String.class, "bar", null, Origin.DATA, true, false); @@ -82,8 +83,8 @@ public void testHasColumnDifferentIndex() { @Test public void testAddAllColumns() { - ColumnStore columnStore = generateEmptyNodeStore(); - IndexImpl index = columnStore.indexStore.mainIndex; + TableImpl nodeTable = generateEmptyNodeTable(); + IndexImpl index = nodeTable.store.indexStore.mainIndex; ColumnImpl col1 = new ColumnImpl("1", String.class, "1", null, Origin.DATA, true, false); ColumnImpl col2 = new ColumnImpl("2", String.class, "2", null, Origin.DATA, false, false); ColumnImpl col3 = new ColumnImpl("3", String.class, "3", null, Origin.DATA, true, false); @@ -97,8 +98,8 @@ public void testAddAllColumns() { @Test public void testDestroy() { - ColumnStore columnStore = generateEmptyNodeStore(); - IndexImpl index = columnStore.indexStore.mainIndex; + TableImpl nodeTable = generateEmptyNodeTable(); + IndexImpl index = nodeTable.store.indexStore.mainIndex; ColumnImpl col1 = new ColumnImpl("1", String.class, "1", null, Origin.DATA, true, false); ColumnImpl col2 = new ColumnImpl("2", String.class, "2", null, Origin.DATA, false, false); col1.setStoreId(0); @@ -130,8 +131,8 @@ public void testDefaultColumns() { Assert.assertNotNull(edgeIndex.getIndex(graphStore.getModel().defaultColumns().edgeTimeSet())); } - private ColumnStore generateEmptyNodeStore() { + private TableImpl generateEmptyNodeTable() { GraphStore graphStore = GraphGenerator.generateEmptyGraphStore(); - return graphStore.nodeTable.store; + return graphStore.nodeTable; } } diff --git a/src/test/java/org/gephi/graph/impl/IntervalIndexImplTest.java b/src/test/java/org/gephi/graph/impl/IntervalIndexImplTest.java index 14a29d82..ef0a53f0 100644 --- a/src/test/java/org/gephi/graph/impl/IntervalIndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/IntervalIndexImplTest.java @@ -31,8 +31,7 @@ public class IntervalIndexImplTest { @Test public void testGetMin() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timeStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timeStore.nodeIndexStore; @@ -73,8 +72,7 @@ public void testGetMinMaxWithView() { @Test public void testGetMax() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timeStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timeStore.nodeIndexStore; @@ -92,8 +90,7 @@ public void testGetMax() { @Test public void testGetMinWithInfinite() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timeStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timeStore.nodeIndexStore; @@ -107,8 +104,7 @@ public void testGetMinWithInfinite() { @Test public void testGetMaxWithInfinite() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timeStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timeStore.nodeIndexStore; @@ -122,8 +118,7 @@ public void testGetMaxWithInfinite() { @Test public void testGetElements() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timeStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timeStore.nodeIndexStore; @@ -170,8 +165,7 @@ public void testGetElements() { @Test public void testHasNodesEdgesEmpty() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timeStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timeStore.nodeIndexStore; @@ -180,8 +174,7 @@ public void testHasNodesEdgesEmpty() { @Test public void testHasNodes() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timeStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timeStore.nodeIndexStore; @@ -204,8 +197,7 @@ public void testHasNodes() { @Test public void testHasNodesClear() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timeStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timeStore.nodeIndexStore; diff --git a/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java index a1a6c11d..9730f494 100644 --- a/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java @@ -261,8 +261,7 @@ public void testRemoveElement() { @Test public void testIndexNode() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -281,8 +280,7 @@ public void testIndexNode() { @Test public void testIndexNodeAdd() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -301,8 +299,7 @@ public void testIndexNodeAdd() { @Test public void testClearNode() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -323,8 +320,7 @@ public void testClearNode() { @Test public void testClearNodeWithAttributes() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -341,8 +337,7 @@ public void testClearNodeWithAttributes() { @Test public void testClearRemove() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -362,8 +357,7 @@ public void testClearRemove() { @Test public void testAddAfterAdd() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -383,8 +377,7 @@ public void testAddAfterAdd() { @Test public void testRemoveAfterAdd() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -409,8 +402,7 @@ public void testRemoveAfterAdd() { @Test public void testSetAttribute() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -437,8 +429,7 @@ public void testSetAttribute() { @Test public void testRemoveAttributeTimestamp() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -455,8 +446,7 @@ public void testRemoveAttributeTimestamp() { @Test public void testAddWithAttribute() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphStore graphStore = new GraphModelImpl(config).store; TimeStore timestampStore = graphStore.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -492,8 +482,7 @@ public void testCreateView() { @Test(expectedExceptions = IllegalArgumentException.class) public void testCreateViewMainView() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; @@ -502,8 +491,7 @@ public void testCreateViewMainView() { @Test(expectedExceptions = IllegalArgumentException.class) public void testDeleteViewMainView() { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); TimeStore timestampStore = graphModel.store.timeStore; IntervalIndexStore store = (IntervalIndexStore) timestampStore.nodeIndexStore; diff --git a/src/test/java/org/gephi/graph/impl/NodeImplTest.java b/src/test/java/org/gephi/graph/impl/NodeImplTest.java new file mode 100644 index 00000000..6cf17669 --- /dev/null +++ b/src/test/java/org/gephi/graph/impl/NodeImplTest.java @@ -0,0 +1,26 @@ +package org.gephi.graph.impl; + +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.Node; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class NodeImplTest { + + @Test + public void testProperties() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + Node n = graphStore.getNode("1"); + Assert.assertNotNull(n.getTextProperties()); + Assert.assertNotNull(n.getColor()); + Assert.assertEquals(n.alpha(), 1f); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testPropertiesDisabled() { + GraphStore graphStore = GraphGenerator + .generateTinyGraphStore(Configuration.builder().enableNodeProperties(false).build()); + Node n = graphStore.getNode("1"); + Assert.assertNull(n.getColor()); + } +} diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 9a07b019..7b50efca 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -713,8 +713,7 @@ public void testTimestampStore() throws IOException, ClassNotFoundException { @Test public void testIntervalStore() throws IOException, ClassNotFoundException { - Configuration config = new Configuration(); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); GraphModelImpl graphModel = new GraphModelImpl(config); GraphStore store = graphModel.store; TimeStore timestampStore = store.timeStore; @@ -737,20 +736,15 @@ public void testIntervalStore() throws IOException, ClassNotFoundException { @Test public void testConfiguration() throws IOException, ClassNotFoundException { - GraphModelImpl graphModel = new GraphModelImpl(); - Configuration configuration = graphModel.configuration; - - configuration.setNodeIdType(Float.class); - configuration.setEdgeIdType(Long.class); - configuration.setTimeRepresentation(TimeRepresentation.INTERVAL); + GraphModelImpl graphModel = new GraphModelImpl(Configuration.builder().nodeIdType(Float.class) + .edgeIdType(Long.class).timeRepresentation(TimeRepresentation.INTERVAL).build()); Serialization ser = new Serialization(graphModel); - byte[] buf = ser.serialize(configuration); + byte[] buf = ser.serialize(graphModel.configuration); - graphModel = new GraphModelImpl(); - ser = new Serialization(graphModel); - Configuration l = (Configuration) ser.deserialize(buf); - Assert.assertTrue(configuration.equals(l)); + ser = new Serialization(new GraphModelImpl()); + ConfigurationImpl l = (ConfigurationImpl) ser.deserialize(buf); + Assert.assertTrue(graphModel.configuration.equals(l)); } @Test @@ -1213,6 +1207,7 @@ public void testSmallUndirectedGraphModel() throws Exception { @Test public void testDefaultColumns() throws Exception { GraphModelImpl gm = GraphGenerator.generateSmallUndirectedGraphStore().graphModel; + Serialization ser = new Serialization(gm); DataInputOutput dio = new DataInputOutput(); @@ -1223,6 +1218,8 @@ public void testDefaultColumns() throws Exception { Assert.assertSame(read.defaultColumns().nodeId(), read.getNodeTable().getColumn("id")); Assert.assertSame(read.defaultColumns().nodeLabel(), read.getNodeTable().getColumn("label")); Assert.assertSame(read.defaultColumns().edgeId(), read.getEdgeTable().getColumn("id")); + Assert.assertSame(read.defaultColumns().edgeLabel(), read.getEdgeTable().getColumn("label")); + Assert.assertSame(read.defaultColumns().edgeWeight(), read.getEdgeTable().getColumn("weight")); } @Test diff --git a/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java b/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java index fbb0325f..1868a6a5 100644 --- a/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java @@ -1,15 +1,13 @@ package org.gephi.graph.impl; import java.util.Arrays; +import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; import org.gephi.graph.api.Rect2D; import org.testng.Assert; -import org.testng.SkipException; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; public class SpatialIndexImplTest { @@ -17,22 +15,21 @@ public class SpatialIndexImplTest { private static final float BOUNDS = 1000f; private static final Rect2D BOUNDS_RECT = new Rect2D(-BOUNDS, -BOUNDS, BOUNDS, BOUNDS); - @BeforeMethod - public void setUp() { - if (!GraphStoreConfiguration.ENABLE_SPATIAL_INDEX) { - throw new SkipException("Skip spatial index tests because feature is disabled"); - } + @Test + public void testDisabled() { + GraphStore store = GraphGenerator.generateEmptyGraphStore(); + Assert.assertNull(store.spatialIndex); } @Test public void testGetEdgesEmpty() { - SpatialIndexImpl spatialIndex = new GraphStore().spatialIndex; + SpatialIndexImpl spatialIndex = new GraphStore(null, getConfig()).spatialIndex; Assert.assertTrue(spatialIndex.getEdgesInArea(BOUNDS_RECT).toCollection().isEmpty()); } @Test public void testGetElementsBothNodesVisible() { - GraphStore store = GraphGenerator.generateTinyGraphStore(); + GraphStore store = GraphGenerator.generateTinyGraphStore(getConfig()); NodeImpl n1 = store.getNode("1"); NodeImpl n2 = store.getNode("2"); @@ -45,7 +42,7 @@ public void testGetElementsBothNodesVisible() { @Test public void testGetElementsOneNodeVisible() { - GraphStore store = GraphGenerator.generateTinyGraphStore(); + GraphStore store = GraphGenerator.generateTinyGraphStore(getConfig()); NodeImpl n1 = store.getNode("1"); n1.setPosition(300000f, 300000f); @@ -59,7 +56,7 @@ public void testGetElementsOneNodeVisible() { @Test public void testGetElementsWithoutNodeVisible() { - GraphStore store = GraphGenerator.generateTinyGraphStore(); + GraphStore store = GraphGenerator.generateTinyGraphStore(getConfig()); NodeImpl n1 = store.getNode("1"); n1.setPosition(300000f, 300000f); @@ -74,7 +71,7 @@ public void testGetElementsWithoutNodeVisible() { @Test public void testGetElementsWithSelfLoop() { - GraphStore store = GraphGenerator.generateTinyGraphStoreWithSelfLoop(); + GraphStore store = GraphGenerator.generateTinyGraphStoreWithSelfLoop(getConfig()); NodeImpl n1 = store.getNode("1"); EdgeImpl e = store.getEdge("0"); @@ -91,4 +88,9 @@ private void assertSame(NodeIterable iterable, Node... expected) { private void assertSame(EdgeIterable iterable, Edge... expected) { Assert.assertEquals(iterable.toCollection(), Arrays.asList(expected)); } + + // Configuration with spatial index + private Configuration getConfig() { + return Configuration.builder().enableSpatialIndex(true).build(); + } } diff --git a/src/test/java/org/gephi/graph/impl/TableImplTest.java b/src/test/java/org/gephi/graph/impl/TableImplTest.java index 9ed0422b..ada6e5b4 100644 --- a/src/test/java/org/gephi/graph/impl/TableImplTest.java +++ b/src/test/java/org/gephi/graph/impl/TableImplTest.java @@ -28,7 +28,7 @@ public class TableImplTest { @Test public void testTable() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Assert.assertEquals(table.countColumns(), 0); Assert.assertEquals(table.size(), 0); Assert.assertTrue(table.isEmpty()); @@ -36,7 +36,7 @@ public void testTable() { @Test public void testAddColumnDefault() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); Assert.assertEquals(table.countColumns(), 1); @@ -47,7 +47,7 @@ public void testAddColumnDefault() { @Test public void testAddColumnWithOrigin() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class, Origin.PROPERTY); Assert.assertEquals(col.getOrigin(), Origin.PROPERTY); Assert.assertTrue(col.isProperty()); @@ -55,7 +55,7 @@ public void testAddColumnWithOrigin() { @Test public void testAddColumnWithTitleAndDefaultValue() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", "Foo", Integer.class, 42); Assert.assertEquals(col.getTitle(), "Foo"); Assert.assertEquals(col.getDefaultValue(), 42); @@ -63,13 +63,13 @@ public void testAddColumnWithTitleAndDefaultValue() { @Test(expectedExceptions = IllegalArgumentException.class) public void testUnknownType() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.addColumn("Id", Node.class); } @Test(expectedExceptions = IllegalArgumentException.class) public void testDefaultValueWrongType() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Float defaultValue = 25f; table.addColumn("Id", null, Integer.class, Origin.DATA, defaultValue, false); @@ -77,28 +77,28 @@ public void testDefaultValueWrongType() { @Test(expectedExceptions = NullPointerException.class) public void testOriginCantBeNull() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.addColumn("Id", null, Integer.class, null, 0, false); } @Test(expectedExceptions = NullPointerException.class) public void testOriginCantBeNull2() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.addColumn("Id", Integer.class, null); } @Test(expectedExceptions = NullPointerException.class) public void testTypeClassCantBeNull() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.addColumn("Id", null, null, Origin.DATA, 0, false); } @Test(expectedExceptions = NullPointerException.class) public void testTypeClassCantBeNull2() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.addColumn("Id", null); } @@ -106,7 +106,7 @@ public void testTypeClassCantBeNull2() { @Test public void testIsIndexed() { GraphStore graphStore = new GraphStore(); - TableImpl table = new TableImpl<>(graphStore, Node.class, true); + TableImpl table = new TableImpl<>(graphStore, Node.class); Column col1 = table.addColumn("Id", null, Integer.class, Origin.DATA, null, false); Column col2 = table.addColumn("1", null, Integer.class, Origin.DATA, null, true); Column col3 = table.addColumn("2", null, TimestampIntegerMap.class, Origin.DATA, null, true); @@ -120,7 +120,7 @@ public void testIsIndexed() { @Test public void testGetColumnId() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); Column c1 = table.getColumn("Id"); @@ -131,13 +131,13 @@ public void testGetColumnId() { @Test public void testGetColumnBadId() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Assert.assertNull(table.getColumn("Id")); } @Test public void testGetColumnIndex() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); Column c = table.getColumn(0); @@ -146,7 +146,7 @@ public void testGetColumnIndex() { @Test public void testHasColumn() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.addColumn("Id", Integer.class); Assert.assertTrue(table.hasColumn("Id")); @@ -157,7 +157,7 @@ public void testHasColumn() { @Test public void testContains() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); Assert.assertTrue(table.contains(col)); @@ -165,40 +165,40 @@ public void testContains() { @Test(expectedExceptions = IllegalArgumentException.class) public void testGetColumnBadIndex() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.getColumn(0); } @Test public void testTitleBackFill() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); Assert.assertEquals(col.getTitle(), "Id"); } @Test public void testIdLowercase() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("A", Integer.class); Assert.assertEquals(col.getId(), "a"); } @Test(expectedExceptions = IllegalArgumentException.class) public void testNonStandardType() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.addColumn("Id", Color.class); } @Test public void testStandardizePrimitiveType() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", int.class); Assert.assertEquals(col.getTypeClass(), Integer.class); } @Test public void testStandardizeArrayType() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("test_col"/* * Id does not allow non-simple types */, Integer[].class); @@ -207,7 +207,7 @@ public void testStandardizeArrayType() { @Test public void testStandardizeArrayDefaultValue() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Integer[] t = new Integer[] { 1, 2 }; Column col = table.addColumn("test_col"/* @@ -220,7 +220,7 @@ public void testStandardizeArrayDefaultValue() { @Test public void testRemoveColumn() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); table.removeColumn(col); @@ -230,7 +230,7 @@ public void testRemoveColumn() { @Test public void testRemove() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); table.remove(col); @@ -239,7 +239,7 @@ public void testRemove() { @Test public void testRemoveColumnString() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.addColumn("Id", Integer.class); table.removeColumn("Id"); @@ -253,7 +253,7 @@ public void testRemoveColumnString() { @Test public void testCountColumns() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); table.addColumn("Id", Integer.class); table.removeColumn("Id"); @@ -263,37 +263,37 @@ public void testCountColumns() { @Test public void testGetElementClass() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Assert.assertEquals(table.getElementClass(), Node.class); } @Test public void testToArray() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); Assert.assertEquals(table.toArray(), new Column[] { col }); } @Test public void testToArrayFromCollection() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); Assert.assertEquals(table.toArray(new Column[0]), new Column[] { col }); } @Test public void testToList() { - TableImpl table = new TableImpl<>(Node.class, false); + TableImpl table = new TableImpl<>(Node.class); Column col = table.addColumn("Id", Integer.class); Assert.assertEquals(table.toList(), Arrays.asList(new Column[] { col })); } @Test public void testDeepEquals() { - TableImpl table1 = new TableImpl<>(Node.class, false); + TableImpl table1 = new TableImpl<>(Node.class); table1.addColumn("Id", Integer.class); - TableImpl table2 = new TableImpl<>(Node.class, false); + TableImpl table2 = new TableImpl<>(Node.class); table2.addColumn("Id", Integer.class); Assert.assertTrue(table1.deepEquals(table2)); @@ -301,10 +301,10 @@ public void testDeepEquals() { @Test public void testDeepHashCode() { - TableImpl table1 = new TableImpl<>(Node.class, false); + TableImpl table1 = new TableImpl<>(Node.class); table1.addColumn("Id", Integer.class); - TableImpl table2 = new TableImpl<>(Node.class, false); + TableImpl table2 = new TableImpl<>(Node.class); table2.addColumn("Id", Integer.class); Assert.assertEquals(table1.deepHashCode(), table2.deepHashCode()); diff --git a/src/test/java/org/gephi/graph/impl/TableObserverTest.java b/src/test/java/org/gephi/graph/impl/TableObserverTest.java index fed5174d..7f38be02 100644 --- a/src/test/java/org/gephi/graph/impl/TableObserverTest.java +++ b/src/test/java/org/gephi/graph/impl/TableObserverTest.java @@ -28,7 +28,7 @@ public class TableObserverTest { @Test public void testDefaultObserver() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(false); Assert.assertFalse(tableObserver.destroyed); @@ -40,7 +40,7 @@ public void testDefaultObserver() { @Test public void testObserverAddColumn() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(false); table.addColumn("0", Integer.class); @@ -51,7 +51,7 @@ public void testObserverAddColumn() { @Test public void testObserverRemoveColumn() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); table.addColumn("0", Integer.class); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(false); table.removeColumn("0"); @@ -62,7 +62,7 @@ public void testObserverRemoveColumn() { @Test public void testObserverModifyColumn() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); Column col = table.addColumn("0", TimestampIntegerMap.class); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(false); col.setEstimator(Estimator.MAX); @@ -73,7 +73,7 @@ public void testObserverModifyColumn() { @Test public void testDestroyObserver() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(false); tableObserver.destroy(); @@ -84,21 +84,21 @@ public void testDestroyObserver() { @Test(expectedExceptions = RuntimeException.class) public void testGetDiffWithoutSetting() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(false); tableObserver.getDiff(); } @Test(expectedExceptions = IllegalStateException.class) public void testGetDiffWithoutHasGraphChanged() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(true); tableObserver.getDiff(); } @Test public void testDiffRemoveColumn() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); table.addColumn("0", Integer.class); Column[] columns = table.toArray(); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(true); @@ -115,7 +115,7 @@ public void testDiffRemoveColumn() { @Test public void testDiffAddColumn() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(true); table.addColumn("0", Integer.class); Column[] columns = table.toArray(); @@ -131,7 +131,7 @@ public void testDiffAddColumn() { @Test public void testDiffModifyColumn() { - TableImpl table = new TableImpl(Node.class, false); + TableImpl table = new TableImpl(Node.class); Column col = table.addColumn("0", TimestampIntegerMap.class); TableObserverImpl tableObserver = (TableObserverImpl) table.createTableObserver(true); col.setEstimator(Estimator.AVERAGE); From fdb0d6877bd0e5b798d7cea0cd942b6de12e4b6f Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 1 May 2023 22:30:54 +0200 Subject: [PATCH 124/271] Fix #166 and #171 with improved graph bridge (#172) * Add copy constructors to interval and timestamp map/set * Add AttributeUtile copy * Fix graph bridge copy --- .../org/gephi/graph/api/AttributeUtils.java | 175 ++++++++++++++++-- .../org/gephi/graph/api/Configuration.java | 1 + .../graph/api/types/IntervalBooleanMap.java | 9 + .../graph/api/types/IntervalByteMap.java | 9 + .../graph/api/types/IntervalCharMap.java | 9 + .../graph/api/types/IntervalDoubleMap.java | 9 + .../graph/api/types/IntervalFloatMap.java | 9 + .../graph/api/types/IntervalIntegerMap.java | 9 + .../graph/api/types/IntervalLongMap.java | 9 + .../gephi/graph/api/types/IntervalSet.java | 9 + .../graph/api/types/IntervalShortMap.java | 9 + .../graph/api/types/IntervalStringMap.java | 9 + .../graph/api/types/TimestampBooleanMap.java | 9 + .../graph/api/types/TimestampByteMap.java | 9 + .../graph/api/types/TimestampCharMap.java | 9 + .../graph/api/types/TimestampDoubleMap.java | 9 + .../graph/api/types/TimestampFloatMap.java | 9 + .../graph/api/types/TimestampIntegerMap.java | 9 + .../graph/api/types/TimestampLongMap.java | 9 + .../gephi/graph/api/types/TimestampSet.java | 9 + .../graph/api/types/TimestampShortMap.java | 9 + .../graph/api/types/TimestampStringMap.java | 9 + .../org/gephi/graph/impl/ElementImpl.java | 10 +- .../org/gephi/graph/impl/GraphBridgeImpl.java | 23 +-- .../graph/api/types/IntervalMapTest.java | 29 +++ .../graph/api/types/IntervalSetTest.java | 10 + .../graph/api/types/TimestampMapTest.java | 29 +++ .../graph/api/types/TimestampSetTest.java | 9 + .../gephi/graph/impl/AttributeUtilsTest.java | 87 +++++++++ .../org/gephi/graph/impl/ElementImplTest.java | 9 + .../org/gephi/graph/impl/GraphBridgeTest.java | 47 ++++- 31 files changed, 573 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index a54ef952..1aa24d74 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package org.gephi.graph.api; import it.unimi.dsi.fastutil.booleans.BooleanArrayList; @@ -41,21 +42,6 @@ import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; import it.unimi.dsi.fastutil.shorts.ShortArrayList; import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; -import java.time.format.DateTimeParseException; -import org.gephi.graph.impl.TimestampsParser; -import org.gephi.graph.impl.IntervalsParser; -import org.gephi.graph.impl.FormattingAndParsingUtils; -import org.gephi.graph.api.types.TimestampMap; -import org.gephi.graph.api.types.TimestampShortMap; -import org.gephi.graph.api.types.TimestampLongMap; -import org.gephi.graph.api.types.TimestampSet; -import org.gephi.graph.api.types.TimestampCharMap; -import org.gephi.graph.api.types.TimestampDoubleMap; -import org.gephi.graph.api.types.TimestampBooleanMap; -import org.gephi.graph.api.types.TimestampFloatMap; -import org.gephi.graph.api.types.TimestampStringMap; -import org.gephi.graph.api.types.TimestampByteMap; -import org.gephi.graph.api.types.TimestampIntegerMap; import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; @@ -67,6 +53,7 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; import java.util.Collections; import java.util.HashMap; @@ -88,8 +75,22 @@ import org.gephi.graph.api.types.IntervalStringMap; import org.gephi.graph.api.types.TimeMap; import org.gephi.graph.api.types.TimeSet; +import org.gephi.graph.api.types.TimestampBooleanMap; +import org.gephi.graph.api.types.TimestampByteMap; +import org.gephi.graph.api.types.TimestampCharMap; +import org.gephi.graph.api.types.TimestampDoubleMap; +import org.gephi.graph.api.types.TimestampFloatMap; +import org.gephi.graph.api.types.TimestampIntegerMap; +import org.gephi.graph.api.types.TimestampLongMap; +import org.gephi.graph.api.types.TimestampMap; +import org.gephi.graph.api.types.TimestampSet; +import org.gephi.graph.api.types.TimestampShortMap; +import org.gephi.graph.api.types.TimestampStringMap; import org.gephi.graph.impl.ArraysParser; +import org.gephi.graph.impl.FormattingAndParsingUtils; import org.gephi.graph.impl.GraphStoreConfiguration; +import org.gephi.graph.impl.IntervalsParser; +import org.gephi.graph.impl.TimestampsParser; /** * Set of utility methods to manipulate supported attribute types. @@ -1215,4 +1216,148 @@ public static boolean isNodeColumn(Column colum) { public static boolean isEdgeColumn(Column colum) { return colum.getTable().getElementClass().equals(Edge.class); } + + /** + * Returns a copy of the provided object. + *

+ * The copy is a deep copy for arrays, {@link IntervalSet}, + * {@link TimestampSet}, sets and lists + * + * @param obj object to copy + * @return copy of the provided object + */ + public static Object copy(Object obj) { + if (obj == null) { + return null; + } + Class typeClass = obj.getClass(); + if (!isSupported(typeClass)) { + throw new IllegalArgumentException("Unsupported type " + typeClass.getCanonicalName()); + } + typeClass = getStandardizedType(typeClass); + obj = standardizeValue(obj); + + // Primitive + if (isSimpleType(typeClass)) { + return obj; + } + + // Interval types: + if (typeClass.equals(IntervalSet.class)) { + return new IntervalSet((IntervalSet) obj); + } else if (typeClass.equals(IntervalStringMap.class)) { + return new IntervalStringMap((IntervalStringMap) obj); + } else if (typeClass.equals(IntervalByteMap.class)) { + return new IntervalByteMap((IntervalByteMap) obj); + } else if (typeClass.equals(IntervalShortMap.class)) { + return new IntervalShortMap((IntervalShortMap) obj); + } else if (typeClass.equals(IntervalIntegerMap.class)) { + return new IntervalIntegerMap((IntervalIntegerMap) obj); + } else if (typeClass.equals(IntervalLongMap.class)) { + return new IntervalLongMap((IntervalLongMap) obj); + } else if (typeClass.equals(IntervalFloatMap.class)) { + return new IntervalFloatMap((IntervalFloatMap) obj); + } else if (typeClass.equals(IntervalDoubleMap.class)) { + return new IntervalDoubleMap((IntervalDoubleMap) obj); + } else if (typeClass.equals(IntervalBooleanMap.class)) { + return new IntervalBooleanMap((IntervalBooleanMap) obj); + } else if (typeClass.equals(IntervalCharMap.class)) { + return new IntervalCharMap((IntervalCharMap) obj); + } + + // Timestamp types: + if (typeClass.equals(TimestampSet.class)) { + return new TimestampSet((TimestampSet) obj); + } else if (typeClass.equals(TimestampStringMap.class)) { + return new TimestampStringMap((TimestampStringMap) obj); + } else if (typeClass.equals(TimestampByteMap.class)) { + return new TimestampByteMap((TimestampByteMap) obj); + } else if (typeClass.equals(TimestampShortMap.class)) { + return new TimestampShortMap((TimestampShortMap) obj); + } else if (typeClass.equals(TimestampIntegerMap.class)) { + return new TimestampIntegerMap((TimestampIntegerMap) obj); + } else if (typeClass.equals(TimestampLongMap.class)) { + return new TimestampLongMap((TimestampLongMap) obj); + } else if (typeClass.equals(TimestampFloatMap.class)) { + return new TimestampFloatMap((TimestampFloatMap) obj); + } else if (typeClass.equals(TimestampDoubleMap.class)) { + return new TimestampDoubleMap((TimestampDoubleMap) obj); + } else if (typeClass.equals(TimestampBooleanMap.class)) { + return new TimestampBooleanMap((TimestampBooleanMap) obj); + } else if (typeClass.equals(TimestampCharMap.class)) { + return new TimestampCharMap((TimestampCharMap) obj); + } + + // Array + if (isArrayType(typeClass)) { + Class componentType = typeClass.getComponentType(); + int length = Array.getLength(obj); + Object dest = Array.newInstance(componentType, length); + System.arraycopy(obj, 0, dest, 0, length); + return dest; + } + + // List + if (obj instanceof CharArrayList) { + return new CharArrayList((CharArrayList) obj); + } else if (obj instanceof BooleanArrayList) { + return new BooleanArrayList((BooleanArrayList) obj); + } else if (obj instanceof ByteArrayList) { + return new ByteArrayList((ByteArrayList) obj); + } else if (obj instanceof ShortArrayList) { + return new ShortArrayList((ShortArrayList) obj); + } else if (obj instanceof IntArrayList) { + return new IntArrayList((IntArrayList) obj); + } else if (obj instanceof LongArrayList) { + return new LongArrayList((LongArrayList) obj); + } else if (obj instanceof FloatArrayList) { + return new FloatArrayList((FloatArrayList) obj); + } else if (obj instanceof DoubleArrayList) { + return new DoubleArrayList((DoubleArrayList) obj); + } else if (obj instanceof ObjectArrayList) { + return new ObjectArrayList((ObjectArrayList) obj); + } + + // Map + if (obj instanceof Char2ObjectOpenHashMap) { + return new Char2ObjectOpenHashMap((Char2ObjectOpenHashMap) obj); + } else if (obj instanceof Byte2ObjectOpenHashMap) { + return new Byte2ObjectOpenHashMap((Byte2ObjectOpenHashMap) obj); + } else if (obj instanceof Short2ObjectOpenHashMap) { + return new Short2ObjectOpenHashMap((Short2ObjectOpenHashMap) obj); + } else if (obj instanceof Int2ObjectOpenHashMap) { + return new Int2ObjectOpenHashMap((Int2ObjectOpenHashMap) obj); + } else if (obj instanceof Long2ObjectOpenHashMap) { + return new Long2ObjectOpenHashMap((Long2ObjectOpenHashMap) obj); + } else if (obj instanceof Float2ObjectOpenHashMap) { + return new Float2ObjectOpenHashMap((Float2ObjectOpenHashMap) obj); + } else if (obj instanceof Double2ObjectOpenHashMap) { + return new Double2ObjectOpenHashMap((Double2ObjectOpenHashMap) obj); + } else if (obj instanceof Object2ObjectOpenHashMap) { + return new Object2ObjectOpenHashMap((Object2ObjectOpenHashMap) obj); + } + + // Set + if (obj instanceof CharOpenHashSet) { + return new CharOpenHashSet((CharOpenHashSet) obj); + } else if (obj instanceof BooleanOpenHashSet) { + return new BooleanOpenHashSet((BooleanOpenHashSet) obj); + } else if (obj instanceof ByteOpenHashSet) { + return new ByteOpenHashSet((ByteOpenHashSet) obj); + } else if (obj instanceof ShortOpenHashSet) { + return new ShortOpenHashSet((ShortOpenHashSet) obj); + } else if (obj instanceof IntOpenHashSet) { + return new IntOpenHashSet((IntOpenHashSet) obj); + } else if (obj instanceof LongOpenHashSet) { + return new LongOpenHashSet((LongOpenHashSet) obj); + } else if (obj instanceof FloatOpenHashSet) { + return new FloatOpenHashSet((FloatOpenHashSet) obj); + } else if (obj instanceof DoubleOpenHashSet) { + return new DoubleOpenHashSet((DoubleOpenHashSet) obj); + } else if (obj instanceof ObjectOpenHashSet) { + return new ObjectOpenHashSet((ObjectOpenHashSet) obj); + } + + return obj; + } } diff --git a/src/main/java/org/gephi/graph/api/Configuration.java b/src/main/java/org/gephi/graph/api/Configuration.java index adafe2b0..0c24fd45 100644 --- a/src/main/java/org/gephi/graph/api/Configuration.java +++ b/src/main/java/org/gephi/graph/api/Configuration.java @@ -27,6 +27,7 @@ * create a GraphModel with custom configuration. *

* Create instances by using the builder: + * *

  * Configuration config = Configuration.builder().build();
  * 
diff --git a/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java b/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java index 0d9898c2..7d4980c2 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java @@ -63,6 +63,15 @@ public IntervalBooleanMap(double[] keys, boolean[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public IntervalBooleanMap(IntervalBooleanMap source) { + this(source.array, source.values); + } + /** * Get the value for the given interval. * diff --git a/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java b/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java index 9601e71e..3e922144 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java @@ -63,6 +63,15 @@ public IntervalByteMap(double[] keys, byte[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public IntervalByteMap(IntervalByteMap source) { + this(source.array, source.values); + } + /** * Get the value for the given interval. * diff --git a/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java b/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java index 054579d3..5878cc40 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java @@ -63,6 +63,15 @@ public IntervalCharMap(double[] keys, char[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public IntervalCharMap(IntervalCharMap source) { + this(source.array, source.values); + } + /** * Get the value for the given interval. * diff --git a/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java b/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java index fe209193..045a9893 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java @@ -63,6 +63,15 @@ public IntervalDoubleMap(double[] keys, double[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public IntervalDoubleMap(IntervalDoubleMap source) { + this(source.array, source.values); + } + /** * Get the value for the given interval. * diff --git a/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java b/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java index 8075bfe7..4f24a9c1 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java @@ -64,6 +64,15 @@ public IntervalFloatMap(double[] keys, float[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public IntervalFloatMap(IntervalFloatMap source) { + this(source.array, source.values); + } + /** * Get the value for the given interval. * diff --git a/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java b/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java index 42f717e6..afe912be 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java @@ -63,6 +63,15 @@ public IntervalIntegerMap(double[] keys, int[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public IntervalIntegerMap(IntervalIntegerMap source) { + this(source.array, source.values); + } + /** * Get the value for the given interval. * diff --git a/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java b/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java index d25779da..33203e1e 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java @@ -63,6 +63,15 @@ public IntervalLongMap(double[] keys, long[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public IntervalLongMap(IntervalLongMap source) { + this(source.array, source.values); + } + /** * Get the value for the given interval. * diff --git a/src/main/java/org/gephi/graph/api/types/IntervalSet.java b/src/main/java/org/gephi/graph/api/types/IntervalSet.java index 70ac6285..b498d8ee 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalSet.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalSet.java @@ -65,6 +65,15 @@ public IntervalSet(double[] arr) { size = arr.length / 2; } + /** + * Copy constructor. + * + * @param source set to copy + */ + public IntervalSet(IntervalSet source) { + this(source.array); + } + @Override public boolean add(Interval interval) { return addInner(interval.getLow(), interval.getHigh()) >= 0; diff --git a/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java b/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java index ce6eedcf..31ceb8c7 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java @@ -63,6 +63,15 @@ public IntervalShortMap(double[] keys, short[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public IntervalShortMap(IntervalShortMap source) { + this(source.array, source.values); + } + /** * Get the value for the given interval. * diff --git a/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java b/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java index 968833f3..8b49bd30 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java @@ -62,6 +62,15 @@ public IntervalStringMap(double[] keys, String[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public IntervalStringMap(IntervalStringMap source) { + this(source.array, source.values); + } + @Override public Class getTypeClass() { return String.class; diff --git a/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java b/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java index 38621d01..9b7a73e5 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java @@ -63,6 +63,15 @@ public TimestampBooleanMap(double[] keys, boolean[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public TimestampBooleanMap(TimestampBooleanMap source) { + this(source.array, source.values); + } + /** * Get the value for the given timestamp. * diff --git a/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java b/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java index 76f95176..d5afaa18 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java @@ -62,6 +62,15 @@ public TimestampByteMap(double[] keys, byte[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public TimestampByteMap(TimestampByteMap source) { + this(source.array, source.values); + } + /** * Get the value for the given timestamp. * diff --git a/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java b/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java index 355e84a4..2e368062 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java @@ -63,6 +63,15 @@ public TimestampCharMap(double[] keys, char[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public TimestampCharMap(TimestampCharMap source) { + this(source.array, source.values); + } + /** * Get the value for the given timestamp. * diff --git a/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java b/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java index eebf197a..8c10ad0b 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java @@ -61,6 +61,15 @@ public TimestampDoubleMap(double[] keys, double[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public TimestampDoubleMap(TimestampDoubleMap source) { + this(source.array, source.values); + } + /** * Get the value for the given timestamp index. * diff --git a/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java b/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java index 18ab8886..ef0f613e 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java @@ -63,6 +63,15 @@ public TimestampFloatMap(double[] keys, float[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public TimestampFloatMap(TimestampFloatMap source) { + this(source.array, source.values); + } + /** * Get the value for the given timestamp. * diff --git a/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java b/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java index 7fb42030..b12a9dc1 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java @@ -63,6 +63,15 @@ public TimestampIntegerMap(double[] keys, int[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public TimestampIntegerMap(TimestampIntegerMap source) { + this(source.array, source.values); + } + /** * Get the value for the given timestamp. * diff --git a/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java b/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java index db37c662..665c6c37 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java @@ -63,6 +63,15 @@ public TimestampLongMap(double[] keys, long[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public TimestampLongMap(TimestampLongMap source) { + this(source.array, source.values); + } + /** * Get the value for the given timestamp. * diff --git a/src/main/java/org/gephi/graph/api/types/TimestampSet.java b/src/main/java/org/gephi/graph/api/types/TimestampSet.java index 2eb485e1..dfc1d707 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampSet.java @@ -64,6 +64,15 @@ public TimestampSet(double[] arr) { size = arr.length; } + /** + * Copy constructor. + * + * @param source the set to copy + */ + public TimestampSet(TimestampSet source) { + this(source.array); + } + @Override public boolean add(Double timestamp) { return addInner(timestamp) >= 0; diff --git a/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java b/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java index d2881844..0b807c8f 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java @@ -63,6 +63,15 @@ public TimestampShortMap(double[] keys, short[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public TimestampShortMap(TimestampShortMap source) { + this(source.array, source.values); + } + /** * Get the value for the given timestamp. * diff --git a/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java b/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java index 694365dd..0e3566c0 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java @@ -61,6 +61,15 @@ public TimestampStringMap(double[] keys, String[] vals) { System.arraycopy(vals, 0, values, 0, vals.length); } + /** + * Copy constructor. + * + * @param source the map to copy + */ + public TimestampStringMap(TimestampStringMap source) { + this(source.array, source.values); + } + @Override public Class getTypeClass() { return String.class; diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index 71a2f3d7..b47f66df 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -414,7 +414,7 @@ public boolean hasInterval(Interval interval) { @Override public Iterable getAttributes(Column column) { checkColumn(column); - checkColumnDynamic(column); + checkColumnDynamicAttribute(column); return attributes.getAttributes(column); } @@ -547,11 +547,17 @@ void checkReadOnlyColumn(Column column) { } void checkColumnDynamic(Column column) { - if (!((ColumnImpl) column).isDynamic()) { + if (!column.isDynamic()) { throw new IllegalArgumentException("The column is not dynamic"); } } + void checkColumnDynamicAttribute(Column column) { + if (!column.isDynamicAttribute()) { + throw new IllegalArgumentException("The column is not a dynamic attribute"); + } + } + void checkType(Column column, Object value) { if (value != null) { Class typeClass = column.getTypeClass(); diff --git a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java index 41ee1cf0..34f80814 100644 --- a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; @@ -185,12 +186,14 @@ private void copyTextProperties(TextProperties text, TextProperties textCopy) { textCopy.setColor(text.getColor()); textCopy.setSize(text.getSize()); textCopy.setVisible(text.isVisible()); + textCopy.setText(text.getText()); + textCopy.setDimensions(text.getWidth(), text.getHeight()); } private void copyTimeSet(Element element, Element elementCopy) { Column sourceColumn = element.getTable().getColumn(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); Column destColumn = elementCopy.getTable().getColumn(GraphStoreConfiguration.ELEMENT_TIMESET_INDEX); - elementCopy.setAttribute(destColumn, element.getAttribute(sourceColumn)); + elementCopy.setAttribute(destColumn, AttributeUtils.copy(element.getAttribute(sourceColumn))); } private void copyColumns(TableImpl sourceTable, TableImpl destTable) { @@ -203,26 +206,10 @@ private void copyColumns(TableImpl sourceTable, TableImpl destTable) { } private void copyAttributes(TableImpl sourceTable, TableImpl destTable, Element element, Element elementCopy) { - TimeRepresentation tr = sourceTable.store.graphStore.configuration.getTimeRepresentation(); for (Column col : sourceTable.toArray()) { if (!col.isProperty()) { Column colCopy = destTable.getColumn(col.getId()); - if (col.isDynamic() && tr.equals(TimeRepresentation.TIMESTAMP)) { - for (Map.Entry entry : element.getAttributes(col)) { - Double key = entry.getKey(); - Object value = entry.getValue(); - elementCopy.setAttribute(colCopy, value, key); - } - } else if (col.isDynamic() && tr.equals(TimeRepresentation.INTERVAL)) { - for (Map.Entry entry : element.getAttributes(col)) { - Interval key = entry.getKey(); - Object value = entry.getValue(); - elementCopy.setAttribute(colCopy, value, key); - } - } else { - Object attribute = element.getAttribute(col); - elementCopy.setAttribute(colCopy, attribute); - } + elementCopy.setAttribute(colCopy, AttributeUtils.copy(element.getAttribute(col))); } } } diff --git a/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java b/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java index 79ba798e..e9f9e0ce 100644 --- a/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java +++ b/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java @@ -592,6 +592,29 @@ public void testToStringDatetime() { Assert.assertEquals(mapInf.toString(TimeFormat.DATETIME), "<[-Infinity, Infinity, value]>"); } + @Test + public void testCopy() { + IntervalStringMap strMap = new IntervalStringMap(new double[] { 1.0, 2.0 }, new String[] { "foo" }); + IntervalByteMap byteMap = new IntervalByteMap(new double[] { 1.0, 2.0 }, new byte[] { 1 }); + IntervalShortMap shortMap = new IntervalShortMap(new double[] { 1.0, 2.0 }, new short[] { 1 }); + IntervalIntegerMap intMap = new IntervalIntegerMap(new double[] { 1.0, 2.0 }, new int[] { 1 }); + IntervalLongMap longMap = new IntervalLongMap(new double[] { 1.0, 2.0 }, new long[] { 1 }); + IntervalFloatMap floatMap = new IntervalFloatMap(new double[] { 1.0, 2.0 }, new float[] { 1 }); + IntervalDoubleMap doubleMap = new IntervalDoubleMap(new double[] { 1.0, 2.0 }, new double[] { 1 }); + IntervalBooleanMap boolMap = new IntervalBooleanMap(new double[] { 1.0, 2.0 }, new boolean[] { true }); + IntervalCharMap charMap = new IntervalCharMap(new double[] { 1.0, 2.0 }, new char[] { 'a' }); + + testEqualsButNotSameUnderlyingArrays(new IntervalStringMap(strMap), strMap); + testEqualsButNotSameUnderlyingArrays(new IntervalByteMap(byteMap), byteMap); + testEqualsButNotSameUnderlyingArrays(new IntervalShortMap(shortMap), shortMap); + testEqualsButNotSameUnderlyingArrays(new IntervalIntegerMap(intMap), intMap); + testEqualsButNotSameUnderlyingArrays(new IntervalLongMap(longMap), longMap); + testEqualsButNotSameUnderlyingArrays(new IntervalFloatMap(floatMap), floatMap); + testEqualsButNotSameUnderlyingArrays(new IntervalDoubleMap(doubleMap), doubleMap); + testEqualsButNotSameUnderlyingArrays(new IntervalBooleanMap(boolMap), boolMap); + testEqualsButNotSameUnderlyingArrays(new IntervalCharMap(charMap), charMap); + } + // UTILITY private void testDoubleArrayEquals(double[] a, double[] b) { Assert.assertEquals(a.length, b.length); @@ -600,6 +623,12 @@ private void testDoubleArrayEquals(double[] a, double[] b) { } } + private void testEqualsButNotSameUnderlyingArrays(IntervalMap a, IntervalMap b) { + Assert.assertEquals(a, b); + Assert.assertNotSame(a.array, b.array); + Assert.assertNotSame(a.getValuesArray(), b.getValuesArray()); + } + private IntervalMap[] getAllInstances() { return new IntervalMap[] { new IntervalStringMap(), new IntervalBooleanMap(), new IntervalFloatMap(), new IntervalDoubleMap(), new IntervalIntegerMap(), new IntervalShortMap(), new IntervalLongMap(), new IntervalByteMap(), new IntervalCharMap() }; } diff --git a/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java b/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java index 106b3a95..b3a7c9e0 100644 --- a/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java +++ b/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java @@ -426,4 +426,14 @@ public void testToStringDatetime() { setInf.add(new Interval(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); Assert.assertEquals(setInf.toString(TimeFormat.DATETIME), "<[-Infinity, Infinity]>"); } + + @Test + public void testCopy() { + IntervalSet set1 = new IntervalSet(); + set1.add(new Interval(1.0, 2.0)); + IntervalSet set2 = new IntervalSet(set1); + Assert.assertEquals(set2, set1); + set1.add(new Interval(3.0, 4.0)); + Assert.assertNotEquals(set2, set1); + } } diff --git a/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java b/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java index f22e8573..66aa1ef5 100644 --- a/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java +++ b/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java @@ -694,6 +694,29 @@ public void testToStringDatetime() { Assert.assertEquals(mapInf.toString(TimeFormat.DATETIME), "<[-Infinity, value]; [Infinity, value]>"); } + @Test + public void testCopy() { + TimestampStringMap strMap = new TimestampStringMap(new double[] { 1.0 }, new String[] { "foo" }); + TimestampByteMap byteMap = new TimestampByteMap(new double[] { 1.0 }, new byte[] { 1 }); + TimestampShortMap shortMap = new TimestampShortMap(new double[] { 1.0 }, new short[] { 1 }); + TimestampIntegerMap intMap = new TimestampIntegerMap(new double[] { 1.0 }, new int[] { 1 }); + TimestampLongMap longMap = new TimestampLongMap(new double[] { 1.0 }, new long[] { 1 }); + TimestampFloatMap floatMap = new TimestampFloatMap(new double[] { 1.0 }, new float[] { 1 }); + TimestampDoubleMap doubleMap = new TimestampDoubleMap(new double[] { 1.0 }, new double[] { 1 }); + TimestampCharMap charMap = new TimestampCharMap(new double[] { 1.0 }, new char[] { 'a' }); + TimestampBooleanMap boolMap = new TimestampBooleanMap(new double[] { 1.0 }, new boolean[] { true }); + + testEqualsButNotSameUnderlyingArrays(new TimestampStringMap(strMap), strMap); + testEqualsButNotSameUnderlyingArrays(new TimestampByteMap(byteMap), byteMap); + testEqualsButNotSameUnderlyingArrays(new TimestampShortMap(shortMap), shortMap); + testEqualsButNotSameUnderlyingArrays(new TimestampIntegerMap(intMap), intMap); + testEqualsButNotSameUnderlyingArrays(new TimestampLongMap(longMap), longMap); + testEqualsButNotSameUnderlyingArrays(new TimestampFloatMap(floatMap), floatMap); + testEqualsButNotSameUnderlyingArrays(new TimestampDoubleMap(doubleMap), doubleMap); + testEqualsButNotSameUnderlyingArrays(new TimestampCharMap(charMap), charMap); + testEqualsButNotSameUnderlyingArrays(new TimestampBooleanMap(boolMap), boolMap); + } + // UTILITY private void testDoubleArrayEquals(double[] a, double[] b) { Assert.assertEquals(a.length, b.length); @@ -702,6 +725,12 @@ private void testDoubleArrayEquals(double[] a, double[] b) { } } + private void testEqualsButNotSameUnderlyingArrays(TimestampMap a, TimestampMap b) { + Assert.assertEquals(a, b); + Assert.assertNotSame(a.array, b.array); + Assert.assertNotSame(a.getValuesArray(), b.getValuesArray()); + } + private TimestampMap[] getAllInstances() { return new TimestampMap[] { new TimestampDoubleMap(), new TimestampByteMap(), new TimestampFloatMap(), new TimestampIntegerMap(), new TimestampLongMap(), new TimestampShortMap(), new TimestampStringMap(), new TimestampCharMap(), new TimestampBooleanMap() }; } diff --git a/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java b/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java index c195205f..dc79247c 100644 --- a/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java +++ b/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java @@ -378,6 +378,15 @@ public void testToStringDatetime() { Assert.assertEquals(setInf.toString(TimeFormat.DATETIME), "<[-Infinity, Infinity]>"); } + @Test + public void testCopy() { + TimestampSet set1 = new TimestampSet(); + set1.add(1.0); + TimestampSet set2 = new TimestampSet(set1); + Assert.assertEquals(set2, set1); + Assert.assertNotSame(set2.toPrimitiveArray(), set1.toPrimitiveArray()); + } + // UTILITY private void testDoubleArrayEquals(double[] a, double[] b) { Assert.assertEquals(a.length, b.length); diff --git a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java index 6667ce6c..57e2415b 100644 --- a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java +++ b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java @@ -887,4 +887,91 @@ public void testIsSimpleType() { Assert.assertFalse(AttributeUtils.isSimpleType(TimestampBooleanMap.class)); Assert.assertFalse(AttributeUtils.isSimpleType(IntervalBooleanMap.class)); } + + @Test + public void testCopyNull() { + Assert.assertNull(AttributeUtils.copy(null)); + } + + @Test + public void testCopyPrimitive() { + String str = "foo"; + Assert.assertSame(AttributeUtils.copy(str), str); + int bar = 42; + Assert.assertSame(AttributeUtils.copy(bar), bar); + } + + @Test + public void testCopyIntervalSet() { + IntervalStringMap strMap = new IntervalStringMap(new double[] { 1.0, 2.0 }, new String[] { "foo" }); + IntervalByteMap byteMap = new IntervalByteMap(new double[] { 1.0, 2.0 }, new byte[] { 1 }); + IntervalShortMap shortMap = new IntervalShortMap(new double[] { 1.0, 2.0 }, new short[] { 1 }); + IntervalIntegerMap intMap = new IntervalIntegerMap(new double[] { 1.0, 2.0 }, new int[] { 1 }); + IntervalLongMap longMap = new IntervalLongMap(new double[] { 1.0, 2.0 }, new long[] { 1 }); + IntervalFloatMap floatMap = new IntervalFloatMap(new double[] { 1.0, 2.0 }, new float[] { 1 }); + IntervalDoubleMap doubleMap = new IntervalDoubleMap(new double[] { 1.0, 2.0 }, new double[] { 1 }); + IntervalBooleanMap boolMap = new IntervalBooleanMap(new double[] { 1.0, 2.0 }, new boolean[] { true }); + IntervalCharMap charMap = new IntervalCharMap(new double[] { 1.0, 2.0 }, new char[] { 'a' }); + + assertCopyIsEqualsButNotSame(strMap); + assertCopyIsEqualsButNotSame(byteMap); + assertCopyIsEqualsButNotSame(shortMap); + assertCopyIsEqualsButNotSame(intMap); + assertCopyIsEqualsButNotSame(longMap); + assertCopyIsEqualsButNotSame(floatMap); + assertCopyIsEqualsButNotSame(doubleMap); + assertCopyIsEqualsButNotSame(boolMap); + assertCopyIsEqualsButNotSame(charMap); + } + + @Test + public void testCopyTimestampSet() { + TimestampStringMap strMap = new TimestampStringMap(new double[] { 1.0 }, new String[] { "foo" }); + TimestampByteMap byteMap = new TimestampByteMap(new double[] { 1.0 }, new byte[] { 1 }); + TimestampShortMap shortMap = new TimestampShortMap(new double[] { 1.0 }, new short[] { 1 }); + TimestampIntegerMap intMap = new TimestampIntegerMap(new double[] { 1.0 }, new int[] { 1 }); + TimestampLongMap longMap = new TimestampLongMap(new double[] { 1.0 }, new long[] { 1 }); + TimestampFloatMap floatMap = new TimestampFloatMap(new double[] { 1.0 }, new float[] { 1 }); + TimestampDoubleMap doubleMap = new TimestampDoubleMap(new double[] { 1.0 }, new double[] { 1 }); + TimestampCharMap charMap = new TimestampCharMap(new double[] { 1.0 }, new char[] { 'a' }); + TimestampBooleanMap boolMap = new TimestampBooleanMap(new double[] { 1.0 }, new boolean[] { true }); + + assertCopyIsEqualsButNotSame(strMap); + assertCopyIsEqualsButNotSame(byteMap); + assertCopyIsEqualsButNotSame(shortMap); + assertCopyIsEqualsButNotSame(intMap); + assertCopyIsEqualsButNotSame(longMap); + assertCopyIsEqualsButNotSame(floatMap); + assertCopyIsEqualsButNotSame(doubleMap); + assertCopyIsEqualsButNotSame(charMap); + assertCopyIsEqualsButNotSame(boolMap); + } + + @Test + public void testCopyList() { + List list = new ArrayList(); + list.add("foo"); + assertCopyIsEqualsButNotSame(list); + } + + @Test + public void testCopySet() { + Set set = new HashSet(); + set.add("foo"); + assertCopyIsEqualsButNotSame(set); + } + + @Test + public void testCopyMap() { + Map map = new HashMap(); + map.put("foo", "bar"); + assertCopyIsEqualsButNotSame(map); + } + + // Utility + private void assertCopyIsEqualsButNotSame(Object o) { + Object copy = AttributeUtils.copy(o); + Assert.assertEquals(copy, o); + Assert.assertNotSame(copy, o); + } } diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index 2b37108d..2b058b0e 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -1058,6 +1058,15 @@ public void testGetDynamicAttributesStaticColumn() { node.getAttributes(column); } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testGetDynamicAttributesTimeset() { + GraphStore store = new GraphStore(); + Column column = store.defaultColumns.nodeTimeSet(); + + NodeImpl node = new NodeImpl("0", store); + node.getAttributes(column); + } + @Test public void testGetAttributeInView() { GraphStore store = new GraphStore(); diff --git a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java index fabdd91f..1bc1da3c 100644 --- a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java @@ -29,6 +29,7 @@ import org.gephi.graph.api.types.IntervalIntegerMap; import org.gephi.graph.api.types.TimestampDoubleMap; import org.gephi.graph.api.types.TimestampIntegerMap; +import org.gephi.graph.api.types.TimestampSet; import org.testng.Assert; import org.testng.annotations.Test; @@ -151,6 +152,8 @@ public void testCopyNodeTextProperties() { n1.getTextProperties().setAlpha(0.5f); n1.getTextProperties().setSize(5f); n1.getTextProperties().setVisible(false); + n1.getTextProperties().setText("foo"); + n1.getTextProperties().setDimensions(2f, 3f); GraphStore dest = new GraphStore(); new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); @@ -167,6 +170,8 @@ public void testCopyEdgeTextProperties() { e0.getTextProperties().setAlpha(0.5f); e0.getTextProperties().setSize(5f); e0.getTextProperties().setVisible(false); + e0.getTextProperties().setText("foo"); + e0.getTextProperties().setDimensions(2f, 3f); GraphStore dest = new GraphStore(); new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); @@ -214,7 +219,6 @@ public void testCopyEdgeWeightInterval() { e0.setWeight(42.0, new Interval(1.0, 2.0)); e0.setWeight(5.0, new Interval(3.0, 4.0)); - config.setTimeRepresentation(TimeRepresentation.INTERVAL); GraphModelImpl gm = new GraphModelImpl(config); GraphStore dest = gm.store; new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); @@ -350,6 +354,8 @@ public void testCopyTimestampSet() { Node n1Copy = dest.getNode("1"); Assert.assertTrue(n1Copy.hasTimestamp(42.0)); + n1.addTimestamp(43.0); + Assert.assertFalse(n1Copy.hasTimestamp(43.0)); } @Test @@ -365,5 +371,44 @@ public void testCopyIntervalSet() { Node n1Copy = dest.getNode("1"); Assert.assertTrue(n1Copy.hasInterval(new Interval(1.0, 2.0))); + n1.addInterval(new Interval(3.0, 4.0)); + Assert.assertFalse(n1Copy.hasInterval(new Interval(3.0, 4.0))); + } + + @Test + public void testCopyArray() { + GraphStore source = GraphGenerator.generateTinyGraphStore(); + Column c1 = source.nodeTable.addColumn("foo", int[].class); + Node n1 = source.getNode("1"); + int[] a1 = new int[] { 1, 2, 3 }; + n1.setAttribute(c1, a1); + + GraphModelImpl model = new GraphModelImpl(); + GraphStore dest = model.store; + new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); + + Node n1Copy = dest.getNode("1"); + Assert.assertEquals(n1Copy.getAttribute(c1.getId()), a1); + a1[0] = 4; + Assert.assertNotEquals(n1Copy.getAttribute(c1.getId()), a1); + } + + @Test + public void testCopyOtherTimesetColumn() { + GraphStore source = GraphGenerator.generateTinyGraphStore(); + Column c1 = source.nodeTable.addColumn("foo", TimestampSet.class); + Node n1 = source.getNode("1"); + TimestampSet set = new TimestampSet(); + set.add(42.0); + n1.setAttribute(c1, set); + + GraphModelImpl model = new GraphModelImpl(); + GraphStore dest = model.store; + new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); + + Node n1Copy = dest.getNode("1"); + Assert.assertEquals(n1Copy.getAttribute(c1.getId()), set); + set.add(43.0); + Assert.assertNotEquals(n1Copy.getAttribute(c1.getId()), set); } } From 2d7653f694ce9ae2198ff5ad7b82b7aabfe767dc Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 6 May 2023 21:45:49 +0200 Subject: [PATCH 125/271] Fix #165 --- .../java/org/gephi/graph/impl/EdgeStore.java | 13 +++++- .../org/gephi/graph/impl/EdgeStoreTest.java | 43 +++++++++++++++++++ .../org/gephi/graph/impl/GraphStoreTest.java | 17 ++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 5731d5f1..d60cc48f 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -61,6 +61,7 @@ public class EdgeStore implements Collection, EdgeIterable { protected Object2IntOpenHashMap dictionary; protected Long2ObjectOpenCustomHashMap[] longDictionary; // Stats + protected int typeSize[]; protected int undirectedSize; protected int mutualEdgesSize; protected int[] mutualEdgesTypeSize; @@ -112,6 +113,7 @@ private void initStore() { GraphStoreConfiguration.EDGESTORE_DEFAULT_DICTIONARY_SIZE, GraphStoreConfiguration.EDGESTORE_DICTIONARY_LOAD_FACTOR, new DictionaryHashStrategy()); this.mutualEdgesTypeSize = new int[GraphStoreConfiguration.EDGESTORE_DEFAULT_TYPE_COUNT]; + this.typeSize = new int[GraphStoreConfiguration.EDGESTORE_DEFAULT_TYPE_COUNT]; } private void ensureCapacity(final int capacity) { @@ -207,6 +209,9 @@ private void ensureLongDictionaryCapacity(int type) { int[] newSizeArray = new int[type + 1]; System.arraycopy(mutualEdgesTypeSize, 0, newSizeArray, 0, length); mutualEdgesTypeSize = newSizeArray; + newSizeArray = new int[type + 1]; + System.arraycopy(typeSize, 0, newSizeArray, 0, length); + typeSize = newSizeArray; } } @@ -321,14 +326,14 @@ public int undirectedSize() { public int size(int type) { if (type < longDictionary.length) { - return longDictionary[type].size(); + return typeSize[type]; } return 0; } public int undirectedSize(int type) { if (type < longDictionary.length) { - return longDictionary[type].size() - mutualEdgesTypeSize[type]; + return typeSize[type] - mutualEdgesTypeSize[type]; } return 0; } @@ -585,6 +590,7 @@ public boolean setEdgeType(final Edge e, int type) { edgeTypeStore.registerEdgeType(type); boolean wasMutual = edge.isMutual(); removeFromDico(edge, edge.storeId); + typeSize[oldType]--; removeOutEdge(edge); removeInEdge(edge); @@ -593,6 +599,7 @@ public boolean setEdgeType(final Edge e, int type) { insertInEdge(edge); addToDico(newDico, newDicoValue, edge, longId); + typeSize[type]++; if (viewStore != null) { viewStore.setEdgeType(edge, oldType, wasMutual); @@ -740,6 +747,7 @@ public boolean add(final Edge e) { } size++; + typeSize[type]++; return true; } else if (isValidIndex(edge.storeId) && get(edge.storeId) == edge) { return false; @@ -780,6 +788,7 @@ public boolean remove(final Object o) { target.inDegree--; size--; + typeSize[edge.type]--; garbageSize++; dictionary.remove(edge.getId()); trimDictionary(); diff --git a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java index 71e1b8ae..f6fc2b86 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java @@ -608,10 +608,43 @@ public void testParallel() { edges[i] = e; Assert.assertTrue(edgeStore.add(e)); Assert.assertTrue(edgeStore.contains(e)); + Assert.assertEquals(edgeStore.size(), i + 1); + Assert.assertEquals(edgeStore.size(0), i + 1); } for (int i = 0; i < 5; i++) { Assert.assertTrue(edgeStore.remove(edges[i])); Assert.assertFalse(edgeStore.contains(edges[i])); + Assert.assertEquals(edgeStore.size(), 9 - i); + Assert.assertEquals(edgeStore.size(0), 9 - i); + } + for (int i = 5; i < 10; i++) { + Assert.assertTrue(edgeStore.contains(edges[i])); + } + } + + @Test + public void testParallelUndirected() { + NodeStore nodeStore = new NodeStore(); + NodeImpl n1 = new NodeImpl("0"); + NodeImpl n2 = new NodeImpl("1"); + nodeStore.add(n1); + nodeStore.add(n2); + + EdgeStore edgeStore = new EdgeStore(); + EdgeImpl[] edges = new EdgeImpl[10]; + for (int i = 0; i < 10; i++) { + EdgeImpl e = new EdgeImpl("" + i, n1, n2, 0, 1.0, false); + edges[i] = e; + Assert.assertTrue(edgeStore.add(e)); + Assert.assertTrue(edgeStore.contains(e)); + Assert.assertEquals(edgeStore.size(), i + 1); + Assert.assertEquals(edgeStore.size(0), i + 1); + } + for (int i = 0; i < 5; i++) { + Assert.assertTrue(edgeStore.remove(edges[i])); + Assert.assertFalse(edgeStore.contains(edges[i])); + Assert.assertEquals(edgeStore.size(), 9 - i); + Assert.assertEquals(edgeStore.size(0), 9 - i); } for (int i = 5; i < 10; i++) { Assert.assertTrue(edgeStore.contains(edges[i])); @@ -1268,6 +1301,8 @@ public void testMutualParallel() { Assert.assertTrue(edgeStore.add(e1)); Assert.assertFalse(e1.isMutual()); Assert.assertTrue(edgeStore.add(e2)); + Assert.assertEquals(edgeStore.size(), 2); + Assert.assertEquals(edgeStore.size(0), 2); Assert.assertTrue(edgeStore.add(e3)); Assert.assertTrue(e1.isMutual()); @@ -1277,6 +1312,8 @@ public void testMutualParallel() { Assert.assertEquals(n1.getUndirectedDegree(), 2); Assert.assertEquals(n2.getDegree(), 3); Assert.assertEquals(n2.getUndirectedDegree(), 2); + Assert.assertEquals(edgeStore.size(), 3); + Assert.assertEquals(edgeStore.size(0), 3); Assert.assertTrue(edgeStore.add(e4)); Assert.assertTrue(e4.isMutual()); @@ -1285,18 +1322,24 @@ public void testMutualParallel() { Assert.assertEquals(n1.getUndirectedDegree(), 2); Assert.assertEquals(n2.getDegree(), 4); Assert.assertEquals(n2.getUndirectedDegree(), 2); + Assert.assertEquals(edgeStore.size(), 4); + Assert.assertEquals(edgeStore.size(0), 4); Assert.assertTrue(edgeStore.remove(e1)); Assert.assertEquals(n1.getDegree(), 3); Assert.assertEquals(n1.getUndirectedDegree(), 2); Assert.assertEquals(n2.getDegree(), 3); Assert.assertEquals(n2.getUndirectedDegree(), 2); + Assert.assertEquals(edgeStore.size(), 3); + Assert.assertEquals(edgeStore.size(0), 3); Assert.assertTrue(edgeStore.remove(e2)); Assert.assertEquals(n1.getDegree(), 2); Assert.assertEquals(n1.getUndirectedDegree(), 2); Assert.assertEquals(n2.getDegree(), 2); Assert.assertEquals(n2.getUndirectedDegree(), 2); + Assert.assertEquals(edgeStore.size(), 2); + Assert.assertEquals(edgeStore.size(0), 2); } @Test diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index 5caf9d2d..ec6fb2e4 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -29,6 +29,7 @@ import java.util.stream.Collectors; import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnIterable; +import org.gephi.graph.api.Configuration; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; @@ -503,6 +504,22 @@ public void testAddEdgeWithSameType() { Assert.assertTrue(graphStore.contains(edge2)); } + @Test + public void testAddEdgeWithSameTypeWithoutParallel() { + Configuration configuration = Configuration.builder().enableParallelEdgesSameType(false).build(); + GraphStore graphStore = new GraphStore(null, new ConfigurationImpl(configuration)); + NodeImpl[] nodes = GraphGenerator.generateNodeList(2); + graphStore.addAllNodes(Arrays.asList(nodes)); + + EdgeImpl edge1 = new EdgeImpl("0", graphStore, nodes[0], nodes[1], 0, 1.0, true); + EdgeImpl edge2 = new EdgeImpl("1", graphStore, nodes[0], nodes[1], 0, 1.0, true); + Assert.assertTrue(graphStore.addEdge(edge1)); + Assert.assertFalse(graphStore.addEdge(edge2)); + + Assert.assertTrue(graphStore.contains(edge1)); + Assert.assertFalse(graphStore.contains(edge2)); + } + @Test public void testAddEdgeTypeRegistration() { GraphStore graphStore = new GraphStore(); From c8c5286c2d874a1024ee3358932d2494234f5412 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 7 May 2023 08:18:30 +0200 Subject: [PATCH 126/271] Create dependabot.yml --- .github/dependabot.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..f74700d6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "maven" + directory: "/" + schedule: + interval: "weekly" From 408c45686fcad1e855928cf9491b7ae03ef5e576 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 19:09:39 +0200 Subject: [PATCH 127/271] Bump actions/checkout from 2 to 3 (#178) Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc52e7a2..bc49679f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Maven Central Repository uses: actions/setup-java@v2 with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index e6987c44..8468a48b 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -7,7 +7,7 @@ jobs: build_and_test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up JDK 11 uses: actions/setup-java@v2 with: From 6cd01a6e6b958f6aeeb9b949e8d7b2f01d332ccf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 19:10:26 +0200 Subject: [PATCH 128/271] Bump actions/setup-java from 2 to 3 (#177) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2 to 3. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc49679f..2a35553d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up Maven Central Repository - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: java-version: '11' distribution: 'temurin' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 8468a48b..96347f8d 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Set up JDK 11 - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: java-version: '11' distribution: 'temurin' From 208c40c6e20f398cfecfae2ebdecd936c4204e34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 19:11:26 +0200 Subject: [PATCH 129/271] Bump testng from 7.7.0 to 7.7.1 (#176) Bumps [testng](https://github.com/cbeust/testng) from 7.7.0 to 7.7.1. - [Release notes](https://github.com/cbeust/testng/releases) - [Changelog](https://github.com/testng-team/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/cbeust/testng/compare/7.7.0...7.7.1) --- updated-dependencies: - dependency-name: org.testng:testng dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 76d13a0d..51536f8e 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ org.testng testng - 7.7.0 + 7.7.1 test From a430ce6f72f439bebb3cc56077dceb2133c7cc44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 19:11:46 +0200 Subject: [PATCH 130/271] Bump formatter-maven-plugin from 2.17.0 to 2.22.0 (#173) Bumps [formatter-maven-plugin](https://github.com/revelc/formatter-maven-plugin) from 2.17.0 to 2.22.0. - [Changelog](https://github.com/revelc/formatter-maven-plugin/blob/main/CHANGELOG.md) - [Commits](https://github.com/revelc/formatter-maven-plugin/compare/formatter-maven-plugin-2.17.0...formatter-maven-plugin-2.22.0) --- updated-dependencies: - dependency-name: net.revelc.code.formatter:formatter-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 51536f8e..9c7ad25f 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ net.revelc.code.formatter formatter-maven-plugin - 2.17.0 + 2.22.0 org.codehaus.mojo From e3e1a8cd404eede938c06c75091fa7c2142ba1d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 19:12:04 +0200 Subject: [PATCH 131/271] Bump jacoco-maven-plugin from 0.8.8 to 0.8.10 (#174) Bumps [jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.8 to 0.8.10. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.8...v0.8.10) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9c7ad25f..8e0d84ea 100644 --- a/pom.xml +++ b/pom.xml @@ -110,7 +110,7 @@ org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.10 org.eluder.coveralls From cea82b1056aa683aaf9ffd73f626e43689e5b546 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 7 May 2023 19:16:09 +0200 Subject: [PATCH 132/271] Add badges --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f0b0ba0e..d6cb19cc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # GraphStore [![build](https://github.com/gephi/graphstore/actions/workflows/ci.yml/badge.svg)](https://github.com/gephi/graphstore/actions/workflows/ci.yml) +[![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/apache/maven.svg?label=License)][license] +[![Maven Central](https://img.shields.io/maven-central/v/org.gephi/graphstore.svg?label=Maven%20Central)](https://search.maven.org/artifact/org.gephi/graphstore) [![Coverage Status](https://coveralls.io/repos/gephi/graphstore/badge.svg?branch=master&service=github)](https://coveralls.io/github/gephi/graphstore?branch=master) GraphStore is an in-memory graph structure implementation written in Java. It's designed to be powerful, efficient and robust. It's powering the Gephi software and supports large graphs in intensive applications. From b42a822f69d48f559278ff1a64f004e2a761cc14 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 7 May 2023 19:19:21 +0200 Subject: [PATCH 133/271] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6cb19cc..b3f32399 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # GraphStore [![build](https://github.com/gephi/graphstore/actions/workflows/ci.yml/badge.svg)](https://github.com/gephi/graphstore/actions/workflows/ci.yml) -[![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/apache/maven.svg?label=License)][license] +[![Apache License, Version 2.0, January 2004](https://img.shields.io/github/license/apache/maven.svg?label=License)](https://www.apache.org/licenses/LICENSE-2.0) [![Maven Central](https://img.shields.io/maven-central/v/org.gephi/graphstore.svg?label=Maven%20Central)](https://search.maven.org/artifact/org.gephi/graphstore) [![Coverage Status](https://coveralls.io/repos/gephi/graphstore/badge.svg?branch=master&service=github)](https://coveralls.io/github/gephi/graphstore?branch=master) From 25e4cb4f3aeda097fd638345538eba13911f188a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 May 2023 19:21:09 +0200 Subject: [PATCH 134/271] Bump maven-gpg-plugin from 1.6 to 3.1.0 (#175) Bumps [maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 1.6 to 3.1.0. - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-1.6...maven-gpg-plugin-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8e0d84ea..d5c917aa 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 + 3.1.0 org.sonatype.plugins From 0b0873e326723519b53253a8b5812176106932f4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 17 May 2023 13:22:35 +0200 Subject: [PATCH 135/271] Refactoring of timezone passing #182 --- .../org/gephi/graph/api/AttributeUtils.java | 108 +++++++++--------- .../gephi/graph/api/types/IntervalMap.java | 10 +- .../gephi/graph/api/types/IntervalSet.java | 4 +- .../org/gephi/graph/api/types/TimeMap.java | 6 +- .../org/gephi/graph/api/types/TimeSet.java | 4 +- .../gephi/graph/api/types/TimestampMap.java | 8 +- .../gephi/graph/api/types/TimestampSet.java | 4 +- .../graph/impl/FormattingAndParsingUtils.java | 8 +- .../org/gephi/graph/impl/IntervalsParser.java | 45 ++++---- .../gephi/graph/impl/TimestampsParser.java | 37 +++--- .../graph/api/types/IntervalMapTest.java | 20 ++-- .../graph/api/types/IntervalSetTest.java | 24 ++-- .../graph/api/types/TimestampMapTest.java | 19 ++- .../graph/api/types/TimestampSetTest.java | 20 ++-- .../gephi/graph/impl/AttributeUtilsTest.java | 79 ++++++------- 15 files changed, 192 insertions(+), 204 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index 1aa24d74..9f8a09ac 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -297,30 +297,30 @@ private AttributeUtils() { // Only static methods } - private static DateTimeFormatter getDateTimeFormatterByTimeZone(Map cache, DateTimeFormatter baseFormatter, ZonedDateTime zonedDateTime) { - if (zonedDateTime == null) { + private static DateTimeFormatter getDateTimeFormatterByTimeZone(Map cache, DateTimeFormatter baseFormatter, ZoneId zoneId) { + if (zoneId == null) { return baseFormatter; } - DateTimeFormatter formatter = cache.get(zonedDateTime.getZone()); + DateTimeFormatter formatter = cache.get(zoneId); if (formatter == null) { - formatter = baseFormatter.withZone(zonedDateTime.getZone()); - cache.put(zonedDateTime.getZone(), formatter); + formatter = baseFormatter.withZone(zoneId); + cache.put(zoneId, formatter); } return formatter; } - private static DateTimeFormatter getDateTimeParserByTimeZone(ZonedDateTime zonedDateTime) { - return getDateTimeFormatterByTimeZone(DATE_TIME_PARSERS_BY_TIMEZONE, DATE_TIME_PARSER, zonedDateTime); + private static DateTimeFormatter getDateTimeParserByTimeZone(ZoneId zoneId) { + return getDateTimeFormatterByTimeZone(DATE_TIME_PARSERS_BY_TIMEZONE, DATE_TIME_PARSER, zoneId); } - private static DateTimeFormatter getDateTimePrinterByTimeZone(ZonedDateTime zonedDateTime) { - return getDateTimeFormatterByTimeZone(DATE_TIME_PRINTERS_BY_TIMEZONE, DATE_TIME_PRINTER, zonedDateTime); + private static DateTimeFormatter getDateTimePrinterByTimeZone(ZoneId zoneId) { + return getDateTimeFormatterByTimeZone(DATE_TIME_PRINTERS_BY_TIMEZONE, DATE_TIME_PRINTER, zoneId); } - private static DateTimeFormatter getDatePrinterByTimeZone(ZonedDateTime zonedDateTime) { - return getDateTimeFormatterByTimeZone(DATE_PRINTERS_BY_TIMEZONE, DATE_PRINTER, zonedDateTime); + private static DateTimeFormatter getDatePrinterByTimeZone(ZoneId zoneId) { + return getDateTimeFormatterByTimeZone(DATE_PRINTERS_BY_TIMEZONE, DATE_PRINTER, zoneId); } /** @@ -338,18 +338,18 @@ public static String print(Object value) { * * @param value value * @param timeFormat time format - * @param zonedDateTime zoned date time + * @param zoneId time zone * @return string representation */ - public static String print(Object value, TimeFormat timeFormat, ZonedDateTime zonedDateTime) { + public static String print(Object value, TimeFormat timeFormat, ZoneId zoneId) { if (value == null) { return "null"; } if (value instanceof TimeSet) { - return ((TimeSet) value).toString(timeFormat, zonedDateTime); + return ((TimeSet) value).toString(timeFormat, zoneId); } if (value instanceof TimeMap) { - return ((TimeMap) value).toString(timeFormat, zonedDateTime); + return ((TimeMap) value).toString(timeFormat, zoneId); } if (value.getClass().isArray()) { return printArray(value); @@ -363,12 +363,12 @@ public static String print(Object value, TimeFormat timeFormat, ZonedDateTime zo * * @param str string to parse * @param typeClass class of the desired type - * @param zonedDateTime time zone to use or null to use default time zone (UTC), - * for dynamic types only + * @param zoneId time zone to use or null to use default time zone (UTC), for + * dynamic types only * @return an instance of the type class, or null if str is null or * empty */ - public static Object parse(String str, Class typeClass, ZonedDateTime zonedDateTime) { + public static Object parse(String str, Class typeClass, ZoneId zoneId) { if (str == null || str.isEmpty()) { return null; } @@ -419,48 +419,48 @@ public static Object parse(String str, Class typeClass, ZonedDateTime zonedDateT // Interval types: if (typeClass.equals(IntervalSet.class)) { - return IntervalsParser.parseIntervalSet(str, zonedDateTime); + return IntervalsParser.parseIntervalSet(str, zoneId); } else if (typeClass.equals(IntervalStringMap.class)) { - return IntervalsParser.parseIntervalMap(String.class, str, zonedDateTime); + return IntervalsParser.parseIntervalMap(String.class, str, zoneId); } else if (typeClass.equals(IntervalByteMap.class)) { - return IntervalsParser.parseIntervalMap(Byte.class, str, zonedDateTime); + return IntervalsParser.parseIntervalMap(Byte.class, str, zoneId); } else if (typeClass.equals(IntervalShortMap.class)) { - return IntervalsParser.parseIntervalMap(Short.class, str, zonedDateTime); + return IntervalsParser.parseIntervalMap(Short.class, str, zoneId); } else if (typeClass.equals(IntervalIntegerMap.class)) { - return IntervalsParser.parseIntervalMap(Integer.class, str, zonedDateTime); + return IntervalsParser.parseIntervalMap(Integer.class, str, zoneId); } else if (typeClass.equals(IntervalLongMap.class)) { - return IntervalsParser.parseIntervalMap(Long.class, str, zonedDateTime); + return IntervalsParser.parseIntervalMap(Long.class, str, zoneId); } else if (typeClass.equals(IntervalFloatMap.class)) { - return IntervalsParser.parseIntervalMap(Float.class, str, zonedDateTime); + return IntervalsParser.parseIntervalMap(Float.class, str, zoneId); } else if (typeClass.equals(IntervalDoubleMap.class)) { - return IntervalsParser.parseIntervalMap(Double.class, str, zonedDateTime); + return IntervalsParser.parseIntervalMap(Double.class, str, zoneId); } else if (typeClass.equals(IntervalBooleanMap.class)) { - return IntervalsParser.parseIntervalMap(Boolean.class, str, zonedDateTime); + return IntervalsParser.parseIntervalMap(Boolean.class, str, zoneId); } else if (typeClass.equals(IntervalCharMap.class)) { - return IntervalsParser.parseIntervalMap(Character.class, str, zonedDateTime); + return IntervalsParser.parseIntervalMap(Character.class, str, zoneId); } // Timestamp types: if (typeClass.equals(TimestampSet.class)) { - return TimestampsParser.parseTimestampSet(str, zonedDateTime); + return TimestampsParser.parseTimestampSet(str, zoneId); } else if (typeClass.equals(TimestampStringMap.class)) { - return TimestampsParser.parseTimestampMap(String.class, str, zonedDateTime); + return TimestampsParser.parseTimestampMap(String.class, str, zoneId); } else if (typeClass.equals(TimestampByteMap.class)) { - return TimestampsParser.parseTimestampMap(Byte.class, str, zonedDateTime); + return TimestampsParser.parseTimestampMap(Byte.class, str, zoneId); } else if (typeClass.equals(TimestampShortMap.class)) { - return TimestampsParser.parseTimestampMap(Short.class, str, zonedDateTime); + return TimestampsParser.parseTimestampMap(Short.class, str, zoneId); } else if (typeClass.equals(TimestampIntegerMap.class)) { - return TimestampsParser.parseTimestampMap(Integer.class, str, zonedDateTime); + return TimestampsParser.parseTimestampMap(Integer.class, str, zoneId); } else if (typeClass.equals(TimestampLongMap.class)) { - return TimestampsParser.parseTimestampMap(Long.class, str, zonedDateTime); + return TimestampsParser.parseTimestampMap(Long.class, str, zoneId); } else if (typeClass.equals(TimestampFloatMap.class)) { - return TimestampsParser.parseTimestampMap(Float.class, str, zonedDateTime); + return TimestampsParser.parseTimestampMap(Float.class, str, zoneId); } else if (typeClass.equals(TimestampDoubleMap.class)) { - return TimestampsParser.parseTimestampMap(Double.class, str, zonedDateTime); + return TimestampsParser.parseTimestampMap(Double.class, str, zoneId); } else if (typeClass.equals(TimestampBooleanMap.class)) { - return TimestampsParser.parseTimestampMap(Boolean.class, str, zonedDateTime); + return TimestampsParser.parseTimestampMap(Boolean.class, str, zoneId); } else if (typeClass.equals(TimestampCharMap.class)) { - return TimestampsParser.parseTimestampMap(Character.class, str, zonedDateTime); + return TimestampsParser.parseTimestampMap(Character.class, str, zoneId); } // Array types: @@ -1037,12 +1037,12 @@ public static String getTypeName(Class type) { * Parses the given time and returns its milliseconds representation. * * @param dateTime type to parse - * @param zonedDateTime time zone to use or null to use default time zone (UTC) + * @param zoneId time zone to use or null to use default time zone (UTC) * @return milliseconds representation * @throws DateTimeParseException if the time cannot be parsed */ - public static double parseDateTime(String dateTime, ZonedDateTime zonedDateTime) throws DateTimeParseException { - DateTimeFormatter dateTimeParserByTimeZone = getDateTimeParserByTimeZone(zonedDateTime); + public static double parseDateTime(String dateTime, ZoneId zoneId) throws DateTimeParseException { + DateTimeFormatter dateTimeParserByTimeZone = getDateTimeParserByTimeZone(zoneId); Instant instant = dateTimeParserByTimeZone.parse(dateTime, Instant::from); return (double) instant.toEpochMilli(); } @@ -1064,12 +1064,12 @@ public static double parseDateTime(String dateTime) throws DateTimeParseExceptio * Returns the date or timestamp converted to a timestamp in milliseconds. * * @param timeStr Date or timestamp string - * @param zonedDateTime Time zone to use or null to use default time zone (UTC) + * @param zoneId Time zone to use or null to use default time zone (UTC) * @return Timestamp * @throws DateTimeParseException if the time cannot be parsed */ - public static double parseDateTimeOrTimestamp(String timeStr, ZonedDateTime zonedDateTime) throws DateTimeParseException { - return FormattingAndParsingUtils.parseDateTimeOrTimestamp(timeStr, zonedDateTime); + public static double parseDateTimeOrTimestamp(String timeStr, ZoneId zoneId) throws DateTimeParseException { + return FormattingAndParsingUtils.parseDateTimeOrTimestamp(timeStr, zoneId); } /** @@ -1099,15 +1099,15 @@ public static String printTimestamp(double timestamp) { * Returns the date's string representation of the given timestamp. * * @param timestamp time, in milliseconds - * @param zonedDatetime time zone to use or null to use default time zone (UTC) + * @param zoneId time zone to use or null to use default time zone (UTC) * @return formatted date */ - public static String printDate(double timestamp, ZonedDateTime zonedDatetime) { + public static String printDate(double timestamp, ZoneId zoneId) { if (Double.isInfinite(timestamp) || Double.isNaN(timestamp)) { return printTimestamp(timestamp); } Instant ofEpochMilli = Instant.ofEpochMilli((long) timestamp); - DateTimeFormatter datePrinterByTimeZone = getDatePrinterByTimeZone(zonedDatetime); + DateTimeFormatter datePrinterByTimeZone = getDatePrinterByTimeZone(zoneId); ZonedDateTime zonedDateTime = ofEpochMilli.atZone(datePrinterByTimeZone.getZone()); return zonedDateTime.format(datePrinterByTimeZone); } @@ -1127,14 +1127,14 @@ public static String printDate(double timestamp) { * Returns the time's string representation of the given timestamp. * * @param timestamp time, in milliseconds - * @param zonedDateTime time zone to use or null to use default time zone (UTC) + * @param zoneId time zone to use or null to use default time zone (UTC) * @return formatted time */ - public static String printDateTime(double timestamp, ZonedDateTime zonedDateTime) { + public static String printDateTime(double timestamp, ZoneId zoneId) { if (Double.isInfinite(timestamp) || Double.isNaN(timestamp)) { return printTimestamp(timestamp); } - DateTimeFormatter dateTimePrinterByTimeZone = getDateTimePrinterByTimeZone(zonedDateTime); + DateTimeFormatter dateTimePrinterByTimeZone = getDateTimePrinterByTimeZone(zoneId); Instant ofEpochMilli = Instant.ofEpochMilli((long) timestamp); ZonedDateTime zonedDateTime2 = ofEpochMilli.atZone(dateTimePrinterByTimeZone.getZone()); OffsetDateTime time = OffsetDateTime.from(zonedDateTime2); @@ -1157,15 +1157,15 @@ public static String printDateTime(double timestamp) { * * @param timestamp time, in milliseconds * @param timeFormat time format - * @param zonedDateTime time zone to use or null to use default time zone (UTC). + * @param zoneId time zone to use or null to use default time zone (UTC). * @return formatted timestamp */ - public static String printTimestampInFormat(double timestamp, TimeFormat timeFormat, ZonedDateTime zonedDateTime) { + public static String printTimestampInFormat(double timestamp, TimeFormat timeFormat, ZoneId zoneId) { switch (timeFormat) { case DATE: - return AttributeUtils.printDate(timestamp, zonedDateTime); + return AttributeUtils.printDate(timestamp, zoneId); case DATETIME: - return AttributeUtils.printDateTime(timestamp, zonedDateTime); + return AttributeUtils.printDateTime(timestamp, zoneId); case DOUBLE: return AttributeUtils.printTimestamp(timestamp); } diff --git a/src/main/java/org/gephi/graph/api/types/IntervalMap.java b/src/main/java/org/gephi/graph/api/types/IntervalMap.java index 8be74214..10727988 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalMap.java @@ -16,12 +16,12 @@ package org.gephi.graph.api.types; import java.lang.reflect.Array; -import org.gephi.graph.api.Estimator; import java.math.BigDecimal; import java.math.RoundingMode; -import java.time.ZonedDateTime; +import java.time.ZoneId; import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; +import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.impl.FormattingAndParsingUtils; @@ -604,7 +604,7 @@ protected Double getAverageDouble(final Interval interval) { } @Override - public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { + public String toString(TimeFormat timeFormat, ZoneId zoneId) { if (size == 0) { return FormattingAndParsingUtils.EMPTY_VALUE; } @@ -615,9 +615,9 @@ public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { sb.append('<'); for (int i = 0; i < size; i++) { sb.append('['); - sb.append(AttributeUtils.printTimestampInFormat(array[i * 2], timeFormat, timeZone)); + sb.append(AttributeUtils.printTimestampInFormat(array[i * 2], timeFormat, zoneId)); sb.append(", "); - sb.append(AttributeUtils.printTimestampInFormat(array[i * 2 + 1], timeFormat, timeZone)); + sb.append(AttributeUtils.printTimestampInFormat(array[i * 2 + 1], timeFormat, zoneId)); sb.append(", "); String stringValue = values[i].toString(); diff --git a/src/main/java/org/gephi/graph/api/types/IntervalSet.java b/src/main/java/org/gephi/graph/api/types/IntervalSet.java index b498d8ee..c7602efd 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalSet.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalSet.java @@ -15,7 +15,7 @@ */ package org.gephi.graph.api.types; -import java.time.ZonedDateTime; +import java.time.ZoneId; import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Interval; @@ -360,7 +360,7 @@ public boolean equals(Object obj) { } @Override - public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { + public String toString(TimeFormat timeFormat, ZoneId timeZone) { if (size == 0) { return FormattingAndParsingUtils.EMPTY_VALUE; } diff --git a/src/main/java/org/gephi/graph/api/types/TimeMap.java b/src/main/java/org/gephi/graph/api/types/TimeMap.java index d25db48a..4f33d205 100644 --- a/src/main/java/org/gephi/graph/api/types/TimeMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimeMap.java @@ -15,7 +15,7 @@ */ package org.gephi.graph.api.types; -import java.time.ZonedDateTime; +import java.time.ZoneId; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; @@ -137,8 +137,8 @@ public interface TimeMap { * Returns this map as a string. * * @param timeFormat time format - * @param timeZone time zone + * @param zoneId time zone * @return map as string */ - public String toString(TimeFormat timeFormat, ZonedDateTime timeZone); + public String toString(TimeFormat timeFormat, ZoneId zoneId); } diff --git a/src/main/java/org/gephi/graph/api/types/TimeSet.java b/src/main/java/org/gephi/graph/api/types/TimeSet.java index e09f9f08..2a01d03e 100644 --- a/src/main/java/org/gephi/graph/api/types/TimeSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimeSet.java @@ -15,7 +15,7 @@ */ package org.gephi.graph.api.types; -import java.time.ZonedDateTime; +import java.time.ZoneId; import org.gephi.graph.api.TimeFormat; /** @@ -130,5 +130,5 @@ public interface TimeSet { * @param timeZone time zone * @return set as string */ - public String toString(TimeFormat timeFormat, ZonedDateTime timeZone); + public String toString(TimeFormat timeFormat, ZoneId timeZone); } diff --git a/src/main/java/org/gephi/graph/api/types/TimestampMap.java b/src/main/java/org/gephi/graph/api/types/TimestampMap.java index 1e6b02a7..9af6613b 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampMap.java @@ -16,12 +16,12 @@ package org.gephi.graph.api.types; import java.lang.reflect.Array; -import org.gephi.graph.api.Estimator; import java.math.BigDecimal; import java.math.RoundingMode; -import java.time.ZonedDateTime; +import java.time.ZoneId; import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; +import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.impl.FormattingAndParsingUtils; @@ -455,7 +455,7 @@ protected Double getAverageDouble(final Interval interval) { } @Override - public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { + public String toString(TimeFormat timeFormat, ZoneId zoneId) { if (size == 0) { return FormattingAndParsingUtils.EMPTY_VALUE; } @@ -466,7 +466,7 @@ public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { sb.append('<'); for (int i = 0; i < size; i++) { sb.append('['); - sb.append(AttributeUtils.printTimestampInFormat(array[i], timeFormat, timeZone)); + sb.append(AttributeUtils.printTimestampInFormat(array[i], timeFormat, zoneId)); sb.append(", "); String stringValue = values[i].toString(); diff --git a/src/main/java/org/gephi/graph/api/types/TimestampSet.java b/src/main/java/org/gephi/graph/api/types/TimestampSet.java index dfc1d707..4a3c6441 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampSet.java @@ -15,7 +15,7 @@ */ package org.gephi.graph.api.types; -import java.time.ZonedDateTime; +import java.time.ZoneId; import java.util.Arrays; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.TimeFormat; @@ -226,7 +226,7 @@ public boolean equals(Object obj) { } @Override - public String toString(TimeFormat timeFormat, ZonedDateTime timeZone) { + public String toString(TimeFormat timeFormat, ZoneId timeZone) { if (size == 0) { return FormattingAndParsingUtils.EMPTY_VALUE; } diff --git a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java index bd14e00b..0b03be8f 100644 --- a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java +++ b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java @@ -20,7 +20,7 @@ import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; -import java.time.ZonedDateTime; +import java.time.ZoneId; import java.time.format.DateTimeParseException; import org.gephi.graph.api.AttributeUtils; @@ -49,11 +49,11 @@ public final class FormattingAndParsingUtils { * Returns the date or timestamp converted to a timestamp in milliseconds. * * @param timeStr Date or timestamp string - * @param timeZone Time zone to use or null to use default time zone (UTC) + * @param zoneId Time zone to use or null to use default time zone (UTC) * @return Timestamp * @throws DateTimeParseException if the time cannot be parsed */ - public static double parseDateTimeOrTimestamp(String timeStr, ZonedDateTime timeZone) throws DateTimeParseException { + public static double parseDateTimeOrTimestamp(String timeStr, ZoneId zoneId) throws DateTimeParseException { double value; try { // Try first to parse as a single double: @@ -62,7 +62,7 @@ public static double parseDateTimeOrTimestamp(String timeStr, ZonedDateTime time throw new IllegalArgumentException("NaN is not allowed as an interval bound"); } } catch (Exception ex) { - value = AttributeUtils.parseDateTime(timeStr, timeZone); + value = AttributeUtils.parseDateTime(timeStr, zoneId); } return value; diff --git a/src/main/java/org/gephi/graph/impl/IntervalsParser.java b/src/main/java/org/gephi/graph/impl/IntervalsParser.java index 0be6fcfe..43064210 100644 --- a/src/main/java/org/gephi/graph/impl/IntervalsParser.java +++ b/src/main/java/org/gephi/graph/impl/IntervalsParser.java @@ -15,19 +15,21 @@ */ package org.gephi.graph.impl; +import static org.gephi.graph.impl.FormattingAndParsingUtils.COMMA; +import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; +import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_BRACKET; +import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_SQUARE_BRACKET; +import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_BRACKET; +import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_SQUARE_BRACKET; + import java.io.IOException; import java.io.StringReader; -import java.time.ZonedDateTime; +import java.time.ZoneId; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.List; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Interval; -import static org.gephi.graph.impl.FormattingAndParsingUtils.COMMA; -import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_SQUARE_BRACKET; -import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_BRACKET; -import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_SQUARE_BRACKET; -import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_BRACKET; import org.gephi.graph.api.types.IntervalBooleanMap; import org.gephi.graph.api.types.IntervalByteMap; import org.gephi.graph.api.types.IntervalCharMap; @@ -39,7 +41,6 @@ import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.IntervalShortMap; import org.gephi.graph.api.types.IntervalStringMap; -import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; /** *

@@ -94,14 +95,14 @@ public final class IntervalsParser { * Parses a {@link IntervalSet} type with one or more intervals. * * @param input Input string to parse - * @param timeZone Time zone to use or null to use default time zone (UTC) + * @param zoneId Time zone to use or null to use default time zone (UTC) * @return Resulting {@link IntervalSet}, or null if the input equals * '<empty>' or is null * @throws IllegalArgumentException Thrown if there are no intervals in the * input string or bounds cannot be parsed into doubles or * dates/datetimes. */ - public static IntervalSet parseIntervalSet(String input, ZonedDateTime timeZone) throws IllegalArgumentException { + public static IntervalSet parseIntervalSet(String input, ZoneId zoneId) throws IllegalArgumentException { if (input == null) { return null; } @@ -112,7 +113,7 @@ public static IntervalSet parseIntervalSet(String input, ZonedDateTime timeZone) List> intervals; try { - intervals = parseIntervals(null, input, timeZone); + intervals = parseIntervals(null, input, zoneId); } catch (IOException ex) { throw new RuntimeException("Unexpected expection while parsing intervals", ex); } @@ -148,7 +149,7 @@ public static IntervalSet parseIntervalSet(String input) throws IllegalArgumentE * @param typeClass Simple type or {@link IntervalMap} subtype for the result * intervals' values. * @param input Input string to parse - * @param timeZone Time zone to use or null to use default time zone (UTC) + * @param zoneId Time zone to use or null to use default time zone (UTC) * @return Resulting {@link IntervalMap}, or null if the input equals * '<empty>' or is null * @throws IllegalArgumentException Thrown if type class is not supported, any @@ -156,7 +157,7 @@ public static IntervalSet parseIntervalSet(String input) throws IllegalArgumentE * are no intervals in the input string or bounds cannot be parsed into * doubles or dates/datetimes. */ - public static IntervalMap parseIntervalMap(Class typeClass, String input, ZonedDateTime timeZone) throws IllegalArgumentException { + public static IntervalMap parseIntervalMap(Class typeClass, String input, ZoneId zoneId) throws IllegalArgumentException { if (typeClass == null) { throw new IllegalArgumentException("typeClass required"); } @@ -167,7 +168,7 @@ public static IntervalMap parseIntervalMap(Class typeClass, String inp List> intervals; try { - intervals = parseIntervals(typeClass, input, timeZone); + intervals = parseIntervals(typeClass, input, zoneId); } catch (IOException ex) { throw new RuntimeException("Unexpected expection while parsing intervals", ex); } @@ -235,10 +236,10 @@ public static IntervalMap parseIntervalMap(Class typeClass, String inp * @param typeClass Class of the intervals' values or null to parse intervals * without values * @param input Input to parse - * @param timeZone Time zone to use or null to use default time zone (UTC) + * @param zoneId Time zone to use or null to use default time zone (UTC) * @return List of Interval */ - private static List> parseIntervals(Class typeClass, String input, ZonedDateTime timeZone) throws IOException, IllegalArgumentException { + private static List> parseIntervals(Class typeClass, String input, ZoneId zoneId) throws IOException, IllegalArgumentException { if (input == null) { return null; } @@ -266,7 +267,7 @@ private static List> parseIntervals(Class typeClass, switch (c) { case LEFT_BOUND_SQUARE_BRACKET: case LEFT_BOUND_BRACKET: - intervals.add(parseInterval(typeClass, reader, timeZone)); + intervals.add(parseInterval(typeClass, reader, zoneId)); break; default: // Ignore other chars outside of intervals @@ -280,7 +281,7 @@ private static List> parseIntervals(Class typeClass, return intervals; } - private static IntervalWithValue parseInterval(Class typeClass, StringReader reader, ZonedDateTime timeZone) throws IOException { + private static IntervalWithValue parseInterval(Class typeClass, StringReader reader, ZoneId zoneId) throws IOException { ArrayList values = new ArrayList<>(); int r; @@ -290,7 +291,7 @@ private static IntervalWithValue parseInterval(Class typeClass, String switch (c) { case RIGHT_BOUND_SQUARE_BRACKET: case RIGHT_BOUND_BRACKET: - return buildInterval(typeClass, values, timeZone); + return buildInterval(typeClass, values, zoneId); case ' ': case '\t': case '\r': @@ -310,10 +311,10 @@ private static IntervalWithValue parseInterval(Class typeClass, String } } - return buildInterval(typeClass, values, timeZone); + return buildInterval(typeClass, values, zoneId); } - private static IntervalWithValue buildInterval(Class typeClass, ArrayList values, ZonedDateTime timeZone) { + private static IntervalWithValue buildInterval(Class typeClass, ArrayList values, ZoneId zoneId) { if (typeClass == null && values.size() != 2) { throw new IllegalArgumentException("Each interval must have 2 values"); } else if (typeClass != null && values.size() != 3) { @@ -321,8 +322,8 @@ private static IntervalWithValue buildInterval(Class typeClass, ArrayL } try { - double low = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(0), timeZone); - double high = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(1), timeZone); + double low = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(0), zoneId); + double high = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(1), zoneId); if (typeClass == null) { return new IntervalWithValue(low, high, null); diff --git a/src/main/java/org/gephi/graph/impl/TimestampsParser.java b/src/main/java/org/gephi/graph/impl/TimestampsParser.java index c5b57db2..8bd7b090 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampsParser.java +++ b/src/main/java/org/gephi/graph/impl/TimestampsParser.java @@ -15,19 +15,21 @@ */ package org.gephi.graph.impl; -import java.io.IOException; -import java.io.StringReader; -import java.time.ZonedDateTime; -import java.time.format.DateTimeParseException; -import java.util.ArrayList; -import org.gephi.graph.api.AttributeUtils; import static org.gephi.graph.impl.FormattingAndParsingUtils.COMMA; import static org.gephi.graph.impl.FormattingAndParsingUtils.DYNAMIC_TYPE_LEFT_BOUND; import static org.gephi.graph.impl.FormattingAndParsingUtils.DYNAMIC_TYPE_RIGHT_BOUND; +import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_SQUARE_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_SQUARE_BRACKET; + +import java.io.IOException; +import java.io.StringReader; +import java.time.ZoneId; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.types.TimestampBooleanMap; import org.gephi.graph.api.types.TimestampByteMap; import org.gephi.graph.api.types.TimestampCharMap; @@ -39,7 +41,6 @@ import org.gephi.graph.api.types.TimestampSet; import org.gephi.graph.api.types.TimestampShortMap; import org.gephi.graph.api.types.TimestampStringMap; -import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; /** *

@@ -89,14 +90,14 @@ public final class TimestampsParser { * Parses a {@link TimestampSet} type with one or more timestamps. * * @param input Input string to parse - * @param timeZone Time zone to use or null to use default time zone (UTC) + * @param zoneId Time zone to use or null to use default time zone (UTC) * @return Resulting {@link TimestampSet}, or null if the input equals * '<empty>' or is null * @throws IllegalArgumentException Thrown if there are no timestamps in the * input string or bounds cannot be parsed into doubles or * dates/datetimes. */ - public static TimestampSet parseTimestampSet(String input, ZonedDateTime timeZone) throws IllegalArgumentException { + public static TimestampSet parseTimestampSet(String input, ZoneId zoneId) throws IllegalArgumentException { if (input == null) { return null; } @@ -154,7 +155,7 @@ public static TimestampSet parseTimestampSet(String input, ZonedDateTime timeZon try { for (String value : values) { - result.add(FormattingAndParsingUtils.parseDateTimeOrTimestamp(value, timeZone)); + result.add(FormattingAndParsingUtils.parseDateTimeOrTimestamp(value, zoneId)); } } catch (DateTimeParseException ex) { throw new IllegalArgumentException("Invalid timestamp value: " + ex.getMessage(), ex); @@ -186,7 +187,7 @@ public static TimestampSet parseTimestampSet(String input) throws IllegalArgumen * @param typeClass Simple type or {@link TimestampMap} subtype for the result * values. * @param input Input string to parse - * @param timeZone Time zone to use or null to use default time zone (UTC) + * @param zoneId Time zone to use or null to use default time zone (UTC) * @return Resulting {@link TimestampMap}, or null if the input equals * '<empty>' or is null * @throws IllegalArgumentException Thrown if type class is not supported, any @@ -194,7 +195,7 @@ public static TimestampSet parseTimestampSet(String input) throws IllegalArgumen * are no timestamps in the input string or bounds cannot be parsed into * doubles or dates/datetimes. */ - public static TimestampMap parseTimestampMap(Class typeClass, String input, ZonedDateTime timeZone) throws IllegalArgumentException { + public static TimestampMap parseTimestampMap(Class typeClass, String input, ZoneId zoneId) throws IllegalArgumentException { if (typeClass == null) { throw new IllegalArgumentException("typeClass required"); } @@ -248,7 +249,7 @@ public static TimestampMap parseTimestampMap(Class typeClass, String i switch (c) { case LEFT_BOUND_SQUARE_BRACKET: case LEFT_BOUND_BRACKET: - parseTimestampAndValue(typeClass, reader, result, timeZone); + parseTimestampAndValue(typeClass, reader, result, zoneId); break; default: // Ignore other chars outside of bounds @@ -280,7 +281,7 @@ public static TimestampMap parseTimestampMap(Class typeClass, String i return parseTimestampMap(typeClass, input, null); } - private static void parseTimestampAndValue(Class typeClass, StringReader reader, TimestampMap result, ZonedDateTime timeZone) throws IOException { + private static void parseTimestampAndValue(Class typeClass, StringReader reader, TimestampMap result, ZoneId zoneId) throws IOException { ArrayList values = new ArrayList<>(); int r; @@ -290,7 +291,7 @@ private static void parseTimestampAndValue(Class typeClass, StringReader switch (c) { case RIGHT_BOUND_SQUARE_BRACKET: case RIGHT_BOUND_BRACKET: - addTimestampAndValue(typeClass, values, result, timeZone); + addTimestampAndValue(typeClass, values, result, zoneId); return; case ' ': case '\t': @@ -311,16 +312,16 @@ private static void parseTimestampAndValue(Class typeClass, StringReader } } - addTimestampAndValue(typeClass, values, result, timeZone); + addTimestampAndValue(typeClass, values, result, zoneId); } - private static void addTimestampAndValue(Class typeClass, ArrayList values, TimestampMap result, ZonedDateTime timeZone) { + private static void addTimestampAndValue(Class typeClass, ArrayList values, TimestampMap result, ZoneId zoneId) { if (values.size() != 2) { throw new IllegalArgumentException("Each timestamp and value array must have 2 values"); } try { - double timestamp = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(0), timeZone); + double timestamp = FormattingAndParsingUtils.parseDateTimeOrTimestamp(values.get(0), zoneId); String valString = values.get(1); T value = FormattingAndParsingUtils.convertValue(typeClass, valString); diff --git a/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java b/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java index e9f9e0ce..10ed64f3 100644 --- a/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java +++ b/src/test/java/org/gephi/graph/api/types/IntervalMapTest.java @@ -535,12 +535,12 @@ public void testToStringDate() { .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0, foo]; [1342483341000.0, 1342483380000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("UTC"))), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("+03:00"))), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("-03:00"))), "<[2012-02-28, 2012-02-29, foo]; [2012-07-16, 2012-07-16, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZoneId + .of("UTC")), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZoneId + .of("+03:00")), "<[2012-02-29, 2012-03-01, foo]; [2012-07-17, 2012-07-17, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZoneId + .of("-03:00")), "<[2012-02-28, 2012-02-29, foo]; [2012-07-16, 2012-07-16, bar]>"); // Test infinity: IntervalStringMap mapInf = new IntervalStringMap(); @@ -567,10 +567,10 @@ public void testToStringDatetime() { .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0, foo]; [1342487444000.0, 1342487445000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId - .of("UTC"))), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId - .of("+12:30"))), "<[2012-02-29T12:30:00.000+12:30, 2012-03-01T12:30:00.000+12:30, foo]; [2012-07-17T13:40:44.000+12:30, 2012-07-17T13:40:45.000+12:30, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZoneId + .of("UTC")), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z, foo]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZoneId + .of("+12:30")), "<[2012-02-29T12:30:00.000+12:30, 2012-03-01T12:30:00.000+12:30, foo]; [2012-07-17T13:40:44.000+12:30, 2012-07-17T13:40:45.000+12:30, bar]>"); // Test with timezone parsing and UTC printing: IntervalStringMap map2 = new IntervalStringMap(); diff --git a/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java b/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java index b3a7c9e0..6ed67915 100644 --- a/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java +++ b/src/test/java/org/gephi/graph/api/types/IntervalSetTest.java @@ -367,16 +367,16 @@ public void testToStringDate() { .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0]; [1342483341000.0, 1342483380000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("UTC"))), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("+12:00"))), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); + Assert.assertEquals(set1 + .toString(TimeFormat.DATE, ZoneId.of("UTC")), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZoneId + .of("+12:00")), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]>"); set1.add(new Interval(AttributeUtils.parseDateTime("2012-07-18T18:30:00"), AttributeUtils.parseDateTime("2012-07-18T18:30:01"))); - Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime.now(ZoneId - .of("+08:00"))), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]; [2012-07-19, 2012-07-19]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime.now(ZoneId - .of("-10:00"))), "<[2012-02-28, 2012-02-29]; [2012-07-16, 2012-07-16]; [2012-07-18, 2012-07-18]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZoneId + .of("+08:00")), "<[2012-02-29, 2012-03-01]; [2012-07-17, 2012-07-17]; [2012-07-19, 2012-07-19]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZoneId + .of("-10:00")), "<[2012-02-28, 2012-02-29]; [2012-07-16, 2012-07-16]; [2012-07-18, 2012-07-18]>"); // Test infinity: IntervalSet setInf = new IntervalSet(); @@ -402,10 +402,10 @@ public void testToStringDatetime() { .toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330560000000.0]; [1342487444000.0, 1342487445000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId - .of("UTC"))), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId - .of("+12:00"))), "<[2012-02-29T12:00:00.000+12:00, 2012-03-01T12:00:00.000+12:00]; [2012-07-17T13:10:44.000+12:00, 2012-07-17T13:10:45.000+12:00]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZoneId + .of("UTC")), "<[2012-02-29T00:00:00.000Z, 2012-03-01T00:00:00.000Z]; [2012-07-17T01:10:44.000Z, 2012-07-17T01:10:45.000Z]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZoneId + .of("+12:00")), "<[2012-02-29T12:00:00.000+12:00, 2012-03-01T12:00:00.000+12:00]; [2012-07-17T13:10:44.000+12:00, 2012-07-17T13:10:45.000+12:00]>"); // Test with timezone parsing and UTC printing: IntervalSet set2 = new IntervalSet(); diff --git a/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java b/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java index 66aa1ef5..3ed2ab6a 100644 --- a/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java +++ b/src/test/java/org/gephi/graph/api/types/TimestampMapTest.java @@ -643,12 +643,11 @@ public void testToStringDate() { Assert.assertEquals(map1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, foo]; [1330473741000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("UTC"))), "<[2012-02-29, foo]; [2012-02-29, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("+03:00"))), "<[2012-02-29, foo]; [2012-02-29, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("-03:00"))), "<[2012-02-28, foo]; [2012-02-28, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATE, ZoneId.of("UTC")), "<[2012-02-29, foo]; [2012-02-29, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATE, ZoneId.of("+03:00")), "<[2012-02-29, foo]; [2012-02-29, bar]>"); + Assert.assertEquals(map1 + .toString(TimeFormat.DATE, ZoneId.of("-03:00")), "<[2012-02-28, foo]; [2012-02-28, bar]>"); // Test infinity: TimestampStringMap mapInf = new TimestampStringMap(); @@ -672,10 +671,10 @@ public void testToStringDatetime() { Assert.assertEquals(map1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, foo]; [1330477844000.0, bar]>"); // Test with time zone printing: - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZonedDateTime - .now(ZoneId.of("UTC"))), "<[2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, bar]>"); - Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId - .of("-01:30"))), "<[2012-02-28T22:30:00.000-01:30, foo]; [2012-02-28T23:40:44.000-01:30, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZoneId + .of("UTC")), "<[2012-02-29T00:00:00.000Z, foo]; [2012-02-29T01:10:44.000Z, bar]>"); + Assert.assertEquals(map1.toString(TimeFormat.DATETIME, ZoneId + .of("-01:30")), "<[2012-02-28T22:30:00.000-01:30, foo]; [2012-02-28T23:40:44.000-01:30, bar]>"); // Test with timezone parsing and UTC printing: TimestampStringMap map2 = new TimestampStringMap(); diff --git a/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java b/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java index dc79247c..3b801a19 100644 --- a/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java +++ b/src/test/java/org/gephi/graph/api/types/TimestampSetTest.java @@ -324,15 +324,13 @@ public void testToStringDate() { Assert.assertEquals(set1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330473741000.0]>"); // Test with time zone printing: + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZoneId.of("UTC")), "<[2012-02-29, 2012-02-29]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATE, ZoneId.of("+12:00")), "<[2012-02-29, 2012-02-29]>"); + set1.add(AttributeUtils.parseDateTime("2012-07-18T18:30:00")); Assert.assertEquals(set1 - .toString(TimeFormat.DATE, ZonedDateTime.now(ZoneId.of("UTC"))), "<[2012-02-29, 2012-02-29]>"); + .toString(TimeFormat.DATE, ZoneId.of("+08:00")), "<[2012-02-29, 2012-02-29, 2012-07-19]>"); Assert.assertEquals(set1 - .toString(TimeFormat.DATE, ZonedDateTime.now(ZoneId.of("+12:00"))), "<[2012-02-29, 2012-02-29]>"); - set1.add(AttributeUtils.parseDateTime("2012-07-18T18:30:00")); - Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("+08:00"))), "<[2012-02-29, 2012-02-29, 2012-07-19]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATE, ZonedDateTime - .now(ZoneId.of("-10:00"))), "<[2012-02-28, 2012-02-28, 2012-07-18]>"); + .toString(TimeFormat.DATE, ZoneId.of("-10:00")), "<[2012-02-28, 2012-02-28, 2012-07-18]>"); // Test infinity: TimestampSet setInf = new TimestampSet(); @@ -356,10 +354,10 @@ public void testToStringDatetime() { Assert.assertEquals(set1.toString(TimeFormat.DOUBLE), "<[1330473600000.0, 1330477844000.0]>"); // Test with time zone printing: - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZonedDateTime - .now(ZoneId.of("UTC"))), "<[2012-02-29T00:00:00.000Z, 2012-02-29T01:10:44.000Z]>"); - Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZonedDateTime - .now(ZoneId.of("+12:15"))), "<[2012-02-29T12:15:00.000+12:15, 2012-02-29T13:25:44.000+12:15]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZoneId + .of("UTC")), "<[2012-02-29T00:00:00.000Z, 2012-02-29T01:10:44.000Z]>"); + Assert.assertEquals(set1.toString(TimeFormat.DATETIME, ZoneId + .of("+12:15")), "<[2012-02-29T12:15:00.000+12:15, 2012-02-29T13:25:44.000+12:15]>"); // Test with timezone parsing and UTC printing: TimestampSet set2 = new TimestampSet(); diff --git a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java index 57e2415b..8e7cc69f 100644 --- a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java +++ b/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java @@ -249,9 +249,9 @@ public void testParseDynamicTimestampTypesWithTimeZone() { Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00]>", TimestampSet.class), AttributeUtils .parse("<[2015-01-01T00:00:00]>", TimestampSet.class, null)); Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00]>", TimestampSet.class), AttributeUtils - .parse("<[2015-01-01T00:00:00]>", TimestampSet.class, ZonedDateTime.now(ZoneId.of("UTC")))); + .parse("<[2015-01-01T00:00:00]>", TimestampSet.class, ZoneId.of("UTC"))); Assert.assertEquals(AttributeUtils.parse("<[2015-01-01T00:00:00]>", TimestampSet.class), AttributeUtils - .parse("<[2015-01-01T01:30:00]>", TimestampSet.class, ZonedDateTime.now(ZoneId.of("+01:30")))); + .parse("<[2015-01-01T01:30:00]>", TimestampSet.class, ZoneId.of("+01:30"))); // Maps Assert.assertEquals(AttributeUtils @@ -259,12 +259,10 @@ public void testParseDynamicTimestampTypesWithTimeZone() { .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, null)); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, ZonedDateTime - .now(ZoneId.of("UTC")))); + .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class, ZoneId.of("UTC"))); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, val]>", TimestampStringMap.class), AttributeUtils - .parse("<[2015-01-01T01:30:00, val]>", TimestampStringMap.class, ZonedDateTime - .now(ZoneId.of("+01:30")))); + .parse("<[2015-01-01T01:30:00, val]>", TimestampStringMap.class, ZoneId.of("+01:30"))); } @Test @@ -275,12 +273,10 @@ public void testParseDynamicIntervalTypesWithTimeZone() { .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, null)); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, ZonedDateTime - .now(ZoneId.of("UTC")))); + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class, ZoneId.of("UTC"))); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00]>", IntervalSet.class), AttributeUtils - .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00]>", IntervalSet.class, ZonedDateTime - .now(ZoneId.of("-02:00")))); + .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00]>", IntervalSet.class, ZoneId.of("-02:00"))); // Maps Assert.assertEquals(AttributeUtils @@ -288,12 +284,12 @@ public void testParseDynamicIntervalTypesWithTimeZone() { .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, null)); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class), AttributeUtils - .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, ZonedDateTime - .now(ZoneId.of("UTC")))); + .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class, ZoneId + .of("UTC"))); Assert.assertEquals(AttributeUtils .parse("<[2015-01-01T00:00:00, 2015-01-01T02:00:00, val]>", IntervalStringMap.class), AttributeUtils - .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00, val]>", IntervalStringMap.class, ZonedDateTime - .now(ZoneId.of("-02:00")))); + .parse("<[2014-12-31T22:00:00, 2015-01-01T00:00:00, val]>", IntervalStringMap.class, ZoneId + .of("-02:00"))); } @Test(expectedExceptions = IllegalArgumentException.class) @@ -577,10 +573,8 @@ public void testStandardizeValueMixedMapKeyContent() { public void testParseDate() { Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00", null), 0.0); - Assert.assertEquals(AttributeUtils - .parseDateTime("1970-01-01T00:00:00", ZonedDateTime.now(ZoneId.of("UTC"))), 0.0); - Assert.assertEquals(AttributeUtils - .parseDateTime("1970-01-01T01:30:00", ZonedDateTime.now(ZoneId.of("+01:30"))), 0.0); + Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00", ZoneId.of("UTC")), 0.0); + Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T01:30:00", ZoneId.of("+01:30")), 0.0); Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00+00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00Z"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTime("1970-01-01T00:00:00.000+00:00"), 0.0); @@ -592,9 +586,9 @@ public void testParseDate() { AttributeUtils.parseDateTime("20040401"); Assert.assertEquals(AttributeUtils.parseDateTime("2012-09-12T15:04:01"), AttributeUtils - .parseDateTime("2012-09-12T15:04:01", ZonedDateTime.now(ZoneId.of("+00:00")))); + .parseDateTime("2012-09-12T15:04:01", ZoneId.of("+00:00"))); Assert.assertEquals(AttributeUtils.parseDateTime("2012-09-12T15:04:01+03:30"), AttributeUtils - .parseDateTime("2012-09-12T15:04:01", ZonedDateTime.now(ZoneId.of("+03:30")))); + .parseDateTime("2012-09-12T15:04:01", ZoneId.of("+03:30"))); } @Test @@ -612,15 +606,13 @@ public void testParseDateTimeOrTimestamp() { Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("0"), AttributeUtils .parseDateTimeOrTimestamp("1970-01-01T00:00:00Z")); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("0"), AttributeUtils - .parseDateTimeOrTimestamp("1970-01-01T00:00:00", ZonedDateTime.now(ZoneId.of("+00:00")))); + .parseDateTimeOrTimestamp("1970-01-01T00:00:00", ZoneId.of("+00:00"))); // Dates Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00", null), 0.0); - Assert.assertEquals(AttributeUtils - .parseDateTimeOrTimestamp("1970-01-01T00:00:00", ZonedDateTime.now(ZoneId.of("UTC"))), 0.0); - Assert.assertEquals(AttributeUtils - .parseDateTimeOrTimestamp("1970-01-01T01:30:00", ZonedDateTime.now(ZoneId.of("+01:30"))), 0.0); + Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00", ZoneId.of("UTC")), 0.0); + Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T01:30:00", ZoneId.of("+01:30")), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00+00:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00Z"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1970-01-01T00:00:00.000+00:00"), 0.0); @@ -628,9 +620,9 @@ public void testParseDateTimeOrTimestamp() { Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("1969-12-31T22:00:00-02:00"), 0.0); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("2012-09-12T15:04:01"), AttributeUtils - .parseDateTime("2012-09-12T15:04:01", ZonedDateTime.now(ZoneId.of("+00:00")))); + .parseDateTime("2012-09-12T15:04:01", ZoneId.of("+00:00"))); Assert.assertEquals(AttributeUtils.parseDateTimeOrTimestamp("2012-09-12T15:04:01+03:30"), AttributeUtils - .parseDateTime("2012-09-12T15:04:01", ZonedDateTime.now(ZoneId.of("+03:30")))); + .parseDateTime("2012-09-12T15:04:01", ZoneId.of("+03:30"))); } @Test @@ -640,15 +632,15 @@ public void testPrintDate() { Assert.assertEquals(AttributeUtils.printDate(d), date); - Assert.assertEquals(AttributeUtils.printDate(d, ZonedDateTime.now(ZoneId.of("UTC"))), date); + Assert.assertEquals(AttributeUtils.printDate(d, ZoneId.of("UTC")), date); Assert.assertEquals(AttributeUtils.printDate(d, null), date); - Assert.assertEquals(AttributeUtils.printDate(d, ZonedDateTime.now(ZoneId.of("+00:30"))), "2003-01-01");// Still + Assert.assertEquals(AttributeUtils.printDate(d, ZoneId.of("+00:30")), "2003-01-01");// Still // same // day - Assert.assertEquals(AttributeUtils.printDate(d, ZonedDateTime.now(ZoneId.of("+12:00"))), "2003-01-01");// Still + Assert.assertEquals(AttributeUtils.printDate(d, ZoneId.of("+12:00")), "2003-01-01");// Still // same // day - Assert.assertEquals(AttributeUtils.printDate(d, ZonedDateTime.now(ZoneId.of("-00:30"))), "2002-12-31");// Previous + Assert.assertEquals(AttributeUtils.printDate(d, ZoneId.of("-00:30")), "2002-12-31");// Previous // day } @@ -660,19 +652,16 @@ public void testPrintDateTime() { String dateInUTC = AttributeUtils.printDateTime(d); Assert.assertEquals(AttributeUtils.parseDateTime(dateInUTC), d); - Assert.assertEquals(AttributeUtils.printDateTime(d, ZonedDateTime.now(ZoneId.of("UTC"))), dateInUTC); + Assert.assertEquals(AttributeUtils.printDateTime(d, ZoneId.of("UTC")), dateInUTC); Assert.assertEquals(AttributeUtils.printDateTime(d, null), dateInUTC); Assert.assertEquals(AttributeUtils.printDateTime(d), "2003-01-01T08:00:00.000Z"); - Assert.assertEquals(AttributeUtils - .printDateTime(d, ZonedDateTime.now(ZoneId.of("+00:30"))), "2003-01-01T08:30:00.000+00:30"); - Assert.assertEquals(AttributeUtils - .printDateTime(d, ZonedDateTime.now(ZoneId.of("+12:00"))), "2003-01-01T20:00:00.000+12:00"); - Assert.assertEquals(AttributeUtils - .printDateTime(d, ZonedDateTime.now(ZoneId.of("-12:00"))), "2002-12-31T20:00:00.000-12:00"); + Assert.assertEquals(AttributeUtils.printDateTime(d, ZoneId.of("+00:30")), "2003-01-01T08:30:00.000+00:30"); + Assert.assertEquals(AttributeUtils.printDateTime(d, ZoneId.of("+12:00")), "2003-01-01T20:00:00.000+12:00"); + Assert.assertEquals(AttributeUtils.printDateTime(d, ZoneId.of("-12:00")), "2002-12-31T20:00:00.000-12:00"); - Assert.assertEquals(AttributeUtils.printDateTime(AttributeUtils - .parseDateTime("2003-01-01T16:00:00", ZonedDateTime.now(ZoneId.of("+00:00"))), ZonedDateTime - .now(ZoneId.of("+12:00"))), "2003-01-02T04:00:00.000+12:00"); + Assert.assertEquals(AttributeUtils + .printDateTime(AttributeUtils.parseDateTime("2003-01-01T16:00:00", ZoneId.of("+00:00")), ZoneId + .of("+12:00")), "2003-01-02T04:00:00.000+12:00"); } @Test @@ -701,16 +690,16 @@ public void testPrint() { Assert.assertEquals(AttributeUtils.print(ts), ts.toString(TimeFormat.DOUBLE)); Assert.assertEquals(AttributeUtils.print(ts, TimeFormat.DATE, null), ts.toString(TimeFormat.DATE, null)); - Assert.assertEquals(AttributeUtils.print(ts, TimeFormat.DATETIME, ZonedDateTime.now(ZoneId.of("+00:30"))), ts - .toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId.of("+00:30")))); + Assert.assertEquals(AttributeUtils.print(ts, TimeFormat.DATETIME, ZoneId.of("+00:30")), ts + .toString(TimeFormat.DATETIME, ZoneId.of("+00:30"))); TimestampIntegerMap tm = new TimestampIntegerMap(); tm.put(d, 42); Assert.assertEquals(AttributeUtils.print(tm), tm.toString(TimeFormat.DOUBLE)); Assert.assertEquals(AttributeUtils.print(tm, TimeFormat.DATE, null), tm.toString(TimeFormat.DATE, null)); - Assert.assertEquals(AttributeUtils.print(tm, TimeFormat.DATETIME, ZonedDateTime.now(ZoneId.of("+00:30"))), tm - .toString(TimeFormat.DATETIME, ZonedDateTime.now(ZoneId.of("+00:30")))); + Assert.assertEquals(AttributeUtils.print(tm, TimeFormat.DATETIME, ZoneId.of("+00:30")), tm + .toString(TimeFormat.DATETIME, ZoneId.of("+00:30"))); } @Test From 423a716a3a6cae50a90cbeb702c0c48134bc4938 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 17 May 2023 13:28:47 +0200 Subject: [PATCH 136/271] Organise imports --- .../graph/api/types/TimestampFloatMap.java | 2 +- .../graph/api/types/TimestampIntegerMap.java | 1 - .../graph/api/types/TimestampLongMap.java | 1 - .../graph/api/types/TimestampShortMap.java | 1 - .../org/gephi/graph/impl/ArraysParser.java | 13 ++++---- .../java/org/gephi/graph/impl/ColumnImpl.java | 2 +- .../org/gephi/graph/impl/ColumnStore.java | 5 ---- .../java/org/gephi/graph/impl/EdgeImpl.java | 5 ++-- .../org/gephi/graph/impl/EdgeTypeStore.java | 1 - .../org/gephi/graph/impl/GraphBridgeImpl.java | 1 - .../org/gephi/graph/impl/GraphModelImpl.java | 19 +++++------- .../gephi/graph/impl/GraphViewDecorator.java | 2 -- .../org/gephi/graph/impl/GraphViewImpl.java | 4 +-- .../org/gephi/graph/impl/GraphViewStore.java | 2 +- .../org/gephi/graph/impl/Serialization.java | 30 +++++++++---------- .../gephi/graph/impl/SpatialIndexImpl.java | 1 - .../java/org/gephi/graph/impl/TableImpl.java | 5 ++-- .../org/gephi/graph/impl/TimeIndexImpl.java | 4 --- .../gephi/graph/impl/TimestampIndexStore.java | 2 +- .../gephi/graph/impl/UndirectedDecorator.java | 2 -- 20 files changed, 39 insertions(+), 64 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java b/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java index ef0f613e..73998d58 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java @@ -15,8 +15,8 @@ */ package org.gephi.graph.api.types; -import org.gephi.graph.api.Estimator; import java.math.BigDecimal; +import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; /** diff --git a/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java b/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java index b12a9dc1..873c9dac 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java @@ -16,7 +16,6 @@ package org.gephi.graph.api.types; import org.gephi.graph.api.Estimator; -import java.math.BigDecimal; import org.gephi.graph.api.Interval; /** diff --git a/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java b/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java index 665c6c37..33a6e8ec 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java @@ -16,7 +16,6 @@ package org.gephi.graph.api.types; import org.gephi.graph.api.Estimator; -import java.math.BigDecimal; import org.gephi.graph.api.Interval; /** diff --git a/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java b/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java index 0b807c8f..5d5eb2e4 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java @@ -16,7 +16,6 @@ package org.gephi.graph.api.types; import org.gephi.graph.api.Estimator; -import java.math.BigDecimal; import org.gephi.graph.api.Interval; /** diff --git a/src/main/java/org/gephi/graph/impl/ArraysParser.java b/src/main/java/org/gephi/graph/impl/ArraysParser.java index f0863955..0a171f91 100644 --- a/src/main/java/org/gephi/graph/impl/ArraysParser.java +++ b/src/main/java/org/gephi/graph/impl/ArraysParser.java @@ -15,17 +15,18 @@ */ package org.gephi.graph.impl; -import java.io.IOException; -import java.io.StringReader; -import java.lang.reflect.Array; -import java.util.ArrayList; -import org.gephi.graph.api.AttributeUtils; import static org.gephi.graph.impl.FormattingAndParsingUtils.COMMA; +import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_SQUARE_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_SQUARE_BRACKET; -import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Array; +import java.util.ArrayList; +import org.gephi.graph.api.AttributeUtils; /** *

diff --git a/src/main/java/org/gephi/graph/impl/ColumnImpl.java b/src/main/java/org/gephi/graph/impl/ColumnImpl.java index 71760206..7c11b3ed 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnImpl.java @@ -19,9 +19,9 @@ import java.util.List; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; +import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Table; -import org.gephi.graph.api.Estimator; import org.gephi.graph.api.types.TimeMap; import org.gephi.graph.api.types.TimeSet; diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index 0dd98967..7eba744f 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -21,17 +21,12 @@ import it.unimi.dsi.fastutil.shorts.ShortRBTreeSet; import it.unimi.dsi.fastutil.shorts.ShortSortedSet; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; -import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnIterable; -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; -import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; public class ColumnStore implements ColumnIterable { diff --git a/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/src/main/java/org/gephi/graph/impl/EdgeImpl.java index 53cf4b81..d469aaa6 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -15,15 +15,16 @@ */ package org.gephi.graph.impl; +import static org.gephi.graph.impl.GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; + import java.awt.Color; import java.util.Map; import org.gephi.graph.api.Column; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeProperties; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Table; -import static org.gephi.graph.impl.GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; public class EdgeImpl extends ElementImpl implements Edge { diff --git a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java index 884fc17d..55e5f393 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java @@ -22,7 +22,6 @@ import it.unimi.dsi.fastutil.shorts.ShortRBTreeSet; import it.unimi.dsi.fastutil.shorts.ShortSortedSet; import java.util.Arrays; -import org.gephi.graph.api.Configuration; import org.gephi.graph.impl.utils.MapDeepEquals; public class EdgeTypeStore { diff --git a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java index 34f80814..9766eb0d 100644 --- a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java @@ -22,7 +22,6 @@ import java.util.Set; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; -import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; import org.gephi.graph.api.GraphBridge; diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 7829777e..2304493e 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -17,34 +17,29 @@ import java.time.ZoneId; import java.util.Arrays; -import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Element; -import org.gephi.graph.api.Index; -import org.gephi.graph.api.SpatialIndex; -import org.gephi.graph.api.Table; -import org.gephi.graph.api.TimeFormat; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Element; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphBridge; import org.gephi.graph.api.GraphFactory; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphObserver; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Index; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; -import org.gephi.graph.api.Origin; +import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.Subgraph; +import org.gephi.graph.api.Table; +import org.gephi.graph.api.TimeFormat; +import org.gephi.graph.api.TimeIndex; import org.gephi.graph.api.UndirectedGraph; import org.gephi.graph.api.UndirectedSubgraph; -import org.gephi.graph.api.TimeIndex; -import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalDoubleMap; -import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.TimestampDoubleMap; -import org.gephi.graph.api.types.TimestampSet; public class GraphModelImpl implements GraphModel { diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index f949c7f0..76823d09 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -18,12 +18,10 @@ import java.util.Collection; import java.util.Iterator; import java.util.Set; -import java.util.function.Consumer; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Interval; diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index 42b8f592..a797f2a2 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -18,19 +18,17 @@ import cern.colt.bitvector.BitVector; import cern.colt.bitvector.QuickBitVector; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Objects; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.UndirectedSubgraph; import org.gephi.graph.impl.EdgeStore.EdgeInOutIterator; diff --git a/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 6d0d7da5..28b94e50 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -17,11 +17,11 @@ import it.unimi.dsi.fastutil.ints.IntRBTreeSet; import it.unimi.dsi.fastutil.ints.IntSortedSet; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedSubgraph; diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index f35a8312..25a9cea3 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -59,24 +59,13 @@ import java.util.Map; import java.util.Set; import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.GraphModel; -import org.gephi.graph.api.Origin; -import org.gephi.graph.api.Estimator; -import org.gephi.graph.api.TimeFormat; -import org.gephi.graph.api.types.TimestampBooleanMap; -import org.gephi.graph.api.types.TimestampByteMap; -import org.gephi.graph.api.types.TimestampCharMap; -import org.gephi.graph.api.types.TimestampDoubleMap; -import org.gephi.graph.api.types.TimestampFloatMap; -import org.gephi.graph.api.types.TimestampIntegerMap; -import org.gephi.graph.api.types.TimestampLongMap; -import org.gephi.graph.api.types.TimestampSet; -import org.gephi.graph.api.types.TimestampShortMap; -import org.gephi.graph.api.types.TimestampStringMap; -import org.gephi.graph.api.types.TimestampMap; import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Estimator; +import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; +import org.gephi.graph.api.Origin; +import org.gephi.graph.api.TimeFormat; import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalBooleanMap; import org.gephi.graph.api.types.IntervalByteMap; @@ -89,6 +78,17 @@ import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.IntervalShortMap; import org.gephi.graph.api.types.IntervalStringMap; +import org.gephi.graph.api.types.TimestampBooleanMap; +import org.gephi.graph.api.types.TimestampByteMap; +import org.gephi.graph.api.types.TimestampCharMap; +import org.gephi.graph.api.types.TimestampDoubleMap; +import org.gephi.graph.api.types.TimestampFloatMap; +import org.gephi.graph.api.types.TimestampIntegerMap; +import org.gephi.graph.api.types.TimestampLongMap; +import org.gephi.graph.api.types.TimestampMap; +import org.gephi.graph.api.types.TimestampSet; +import org.gephi.graph.api.types.TimestampShortMap; +import org.gephi.graph.api.types.TimestampStringMap; import org.gephi.graph.impl.EdgeImpl.EdgePropertiesImpl; import org.gephi.graph.impl.NodeImpl.NodePropertiesImpl; import org.gephi.graph.impl.utils.DataInputOutput; diff --git a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java index 8ab48ef4..b3d63f6c 100644 --- a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java @@ -1,7 +1,6 @@ package org.gephi.graph.impl; import java.util.Iterator; -import java.util.function.Consumer; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Node; diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index d217dee8..c66ccb4e 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -21,13 +21,12 @@ import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Element; +import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Table; -import org.gephi.graph.api.TableLock; import org.gephi.graph.api.TableObserver; -import org.gephi.graph.api.Element; -import org.gephi.graph.api.Graph; public class TableImpl implements Collection, Table { diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java index e3aa2a00..3e8c50c9 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java @@ -15,14 +15,10 @@ */ package org.gephi.graph.impl; -import it.unimi.dsi.fastutil.objects.ObjectIterator; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Set; import org.gephi.graph.api.Element; import org.gephi.graph.api.ElementIterable; diff --git a/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java b/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java index 55bd083e..f9af6929 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java @@ -16,9 +16,9 @@ package org.gephi.graph.impl; import it.unimi.dsi.fastutil.doubles.Double2IntRBTreeMap; -import org.gephi.graph.api.types.TimestampSet; import org.gephi.graph.api.Element; import org.gephi.graph.api.types.TimestampMap; +import org.gephi.graph.api.types.TimestampSet; public class TimestampIndexStore extends TimeIndexStore> { diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 13ea499a..55af3b70 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -20,13 +20,11 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedGraph; import org.gephi.graph.api.UndirectedSubgraph; From a8bb87972cda72b37fe42fc4e0870e06114a819b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 3 Jun 2023 22:02:26 +0200 Subject: [PATCH 137/271] Support for Instant column type (#185) * Organise imports * Implement support for column instant type --- .../org/gephi/graph/api/AttributeUtils.java | 49 ++++++++++++++-- .../graph/api/types/TimestampFloatMap.java | 2 +- .../graph/api/types/TimestampIntegerMap.java | 1 - .../graph/api/types/TimestampLongMap.java | 1 - .../graph/api/types/TimestampShortMap.java | 1 - .../org/gephi/graph/impl/ArraysParser.java | 13 +++-- .../java/org/gephi/graph/impl/ColumnImpl.java | 2 +- .../org/gephi/graph/impl/ColumnStore.java | 5 -- .../java/org/gephi/graph/impl/EdgeImpl.java | 5 +- .../org/gephi/graph/impl/EdgeTypeStore.java | 1 - .../org/gephi/graph/impl/GraphBridgeImpl.java | 1 - .../org/gephi/graph/impl/GraphModelImpl.java | 19 +++--- .../gephi/graph/impl/GraphViewDecorator.java | 2 - .../org/gephi/graph/impl/GraphViewImpl.java | 4 +- .../org/gephi/graph/impl/GraphViewStore.java | 2 +- .../org/gephi/graph/impl/Serialization.java | 50 +++++++++++----- .../gephi/graph/impl/SpatialIndexImpl.java | 1 - .../java/org/gephi/graph/impl/TableImpl.java | 5 +- .../org/gephi/graph/impl/TimeIndexImpl.java | 4 -- .../gephi/graph/impl/TimestampIndexStore.java | 2 +- .../gephi/graph/impl/UndirectedDecorator.java | 2 - .../{impl => api}/AttributeUtilsTest.java | 58 ++++++++++++++++++- .../org/gephi/graph/impl/ElementImplTest.java | 18 ++++++ .../gephi/graph/impl/SerializationTest.java | 10 ++++ .../org/gephi/graph/impl/TableImplTest.java | 8 +++ 25 files changed, 195 insertions(+), 71 deletions(-) rename src/test/java/org/gephi/graph/{impl => api}/AttributeUtilsTest.java (94%) diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index 9f8a09ac..3dbacf0a 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -146,6 +146,9 @@ public class AttributeUtils { // Objects supportedTypes.add(String.class); + // Instant + supportedTypes.add(Instant.class); + // Primitives Array supportedTypes.add(Boolean[].class); supportedTypes.add(boolean[].class); @@ -351,6 +354,9 @@ public static String print(Object value, TimeFormat timeFormat, ZoneId zoneId) { if (value instanceof TimeMap) { return ((TimeMap) value).toString(timeFormat, zoneId); } + if (value instanceof Instant) { + printDate((Instant) value, zoneId); + } if (value.getClass().isArray()) { return printArray(value); } @@ -364,7 +370,7 @@ public static String print(Object value, TimeFormat timeFormat, ZoneId zoneId) { * @param str string to parse * @param typeClass class of the desired type * @param zoneId time zone to use or null to use default time zone (UTC), for - * dynamic types only + * dynamic types and Instant only * @return an instance of the type class, or null if str is null or * empty */ @@ -417,6 +423,12 @@ public static Object parse(String str, Class typeClass, ZoneId zoneId) { return str.charAt(0); } + // Instant + if (typeClass.equals(Instant.class)) { + double milliseconds = FormattingAndParsingUtils.parseDateTimeOrTimestamp(str, zoneId); + return Instant.ofEpochMilli((long) milliseconds); + } + // Interval types: if (typeClass.equals(IntervalSet.class)) { return IntervalsParser.parseIntervalSet(str, zoneId); @@ -1106,9 +1118,19 @@ public static String printDate(double timestamp, ZoneId zoneId) { if (Double.isInfinite(timestamp) || Double.isNaN(timestamp)) { return printTimestamp(timestamp); } - Instant ofEpochMilli = Instant.ofEpochMilli((long) timestamp); + return printDate(Instant.ofEpochMilli((long) timestamp), zoneId); + } + + /** + * Returns the date's string representation of the given instant. + * + * @param instant instant to format + * @param zoneId time zone to use or null to use default time zone (UTC) + * @return formatted date + */ + public static String printDate(Instant instant, ZoneId zoneId) { DateTimeFormatter datePrinterByTimeZone = getDatePrinterByTimeZone(zoneId); - ZonedDateTime zonedDateTime = ofEpochMilli.atZone(datePrinterByTimeZone.getZone()); + ZonedDateTime zonedDateTime = instant.atZone(datePrinterByTimeZone.getZone()); return zonedDateTime.format(datePrinterByTimeZone); } @@ -1134,15 +1156,25 @@ public static String printDateTime(double timestamp, ZoneId zoneId) { if (Double.isInfinite(timestamp) || Double.isNaN(timestamp)) { return printTimestamp(timestamp); } + return printDateTime(Instant.ofEpochMilli((long) timestamp), zoneId); + } + + /** + * Returns the time's string representation of the given instant. + * + * @param instant instant to format + * @param zoneId time zone to use or null to use default time zone (UTC) + * @return formatted time + */ + public static String printDateTime(Instant instant, ZoneId zoneId) { DateTimeFormatter dateTimePrinterByTimeZone = getDateTimePrinterByTimeZone(zoneId); - Instant ofEpochMilli = Instant.ofEpochMilli((long) timestamp); - ZonedDateTime zonedDateTime2 = ofEpochMilli.atZone(dateTimePrinterByTimeZone.getZone()); + ZonedDateTime zonedDateTime2 = instant.atZone(dateTimePrinterByTimeZone.getZone()); OffsetDateTime time = OffsetDateTime.from(zonedDateTime2); return time.format(dateTimePrinterByTimeZone); } /** - * Returns the time's tring representation of the given timestamp. Default time + * Returns the time's string representation of the given timestamp. Default time * zone is used (UTC). * * @param timestamp time, in milliseconds @@ -1242,6 +1274,11 @@ public static Object copy(Object obj) { return obj; } + // Instant + if (typeClass.equals(Instant.class)) { + return Instant.from((Instant) obj); + } + // Interval types: if (typeClass.equals(IntervalSet.class)) { return new IntervalSet((IntervalSet) obj); diff --git a/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java b/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java index ef0f613e..73998d58 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java @@ -15,8 +15,8 @@ */ package org.gephi.graph.api.types; -import org.gephi.graph.api.Estimator; import java.math.BigDecimal; +import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Interval; /** diff --git a/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java b/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java index b12a9dc1..873c9dac 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java @@ -16,7 +16,6 @@ package org.gephi.graph.api.types; import org.gephi.graph.api.Estimator; -import java.math.BigDecimal; import org.gephi.graph.api.Interval; /** diff --git a/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java b/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java index 665c6c37..33a6e8ec 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java @@ -16,7 +16,6 @@ package org.gephi.graph.api.types; import org.gephi.graph.api.Estimator; -import java.math.BigDecimal; import org.gephi.graph.api.Interval; /** diff --git a/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java b/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java index 0b807c8f..5d5eb2e4 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java @@ -16,7 +16,6 @@ package org.gephi.graph.api.types; import org.gephi.graph.api.Estimator; -import java.math.BigDecimal; import org.gephi.graph.api.Interval; /** diff --git a/src/main/java/org/gephi/graph/impl/ArraysParser.java b/src/main/java/org/gephi/graph/impl/ArraysParser.java index f0863955..0a171f91 100644 --- a/src/main/java/org/gephi/graph/impl/ArraysParser.java +++ b/src/main/java/org/gephi/graph/impl/ArraysParser.java @@ -15,17 +15,18 @@ */ package org.gephi.graph.impl; -import java.io.IOException; -import java.io.StringReader; -import java.lang.reflect.Array; -import java.util.ArrayList; -import org.gephi.graph.api.AttributeUtils; import static org.gephi.graph.impl.FormattingAndParsingUtils.COMMA; +import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_SQUARE_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_SQUARE_BRACKET; -import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; + +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Array; +import java.util.ArrayList; +import org.gephi.graph.api.AttributeUtils; /** *

diff --git a/src/main/java/org/gephi/graph/impl/ColumnImpl.java b/src/main/java/org/gephi/graph/impl/ColumnImpl.java index 71760206..7c11b3ed 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnImpl.java @@ -19,9 +19,9 @@ import java.util.List; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; +import org.gephi.graph.api.Estimator; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Table; -import org.gephi.graph.api.Estimator; import org.gephi.graph.api.types.TimeMap; import org.gephi.graph.api.types.TimeSet; diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index 0dd98967..7eba744f 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -21,17 +21,12 @@ import it.unimi.dsi.fastutil.shorts.ShortRBTreeSet; import it.unimi.dsi.fastutil.shorts.ShortSortedSet; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; -import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnIterable; -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; -import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; public class ColumnStore implements ColumnIterable { diff --git a/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/src/main/java/org/gephi/graph/impl/EdgeImpl.java index 53cf4b81..d469aaa6 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -15,15 +15,16 @@ */ package org.gephi.graph.impl; +import static org.gephi.graph.impl.GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; + import java.awt.Color; import java.util.Map; import org.gephi.graph.api.Column; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeProperties; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Table; -import static org.gephi.graph.impl.GraphStoreConfiguration.DEFAULT_DYNAMIC_EDGE_WEIGHT_WHEN_MISSING; public class EdgeImpl extends ElementImpl implements Edge { diff --git a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java index 884fc17d..55e5f393 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeTypeStore.java @@ -22,7 +22,6 @@ import it.unimi.dsi.fastutil.shorts.ShortRBTreeSet; import it.unimi.dsi.fastutil.shorts.ShortSortedSet; import java.util.Arrays; -import org.gephi.graph.api.Configuration; import org.gephi.graph.impl.utils.MapDeepEquals; public class EdgeTypeStore { diff --git a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java index 34f80814..9766eb0d 100644 --- a/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphBridgeImpl.java @@ -22,7 +22,6 @@ import java.util.Set; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; -import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; import org.gephi.graph.api.GraphBridge; diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 7829777e..2304493e 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -17,34 +17,29 @@ import java.time.ZoneId; import java.util.Arrays; -import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.Element; -import org.gephi.graph.api.Index; -import org.gephi.graph.api.SpatialIndex; -import org.gephi.graph.api.Table; -import org.gephi.graph.api.TimeFormat; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Element; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphBridge; import org.gephi.graph.api.GraphFactory; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphObserver; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Index; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; -import org.gephi.graph.api.Origin; +import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.Subgraph; +import org.gephi.graph.api.Table; +import org.gephi.graph.api.TimeFormat; +import org.gephi.graph.api.TimeIndex; import org.gephi.graph.api.UndirectedGraph; import org.gephi.graph.api.UndirectedSubgraph; -import org.gephi.graph.api.TimeIndex; -import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalDoubleMap; -import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.TimestampDoubleMap; -import org.gephi.graph.api.types.TimestampSet; public class GraphModelImpl implements GraphModel { diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index f949c7f0..76823d09 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -18,12 +18,10 @@ import java.util.Collection; import java.util.Iterator; import java.util.Set; -import java.util.function.Consumer; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Interval; diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index 42b8f592..a797f2a2 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -18,19 +18,17 @@ import cern.colt.bitvector.BitVector; import cern.colt.bitvector.QuickBitVector; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Objects; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.UndirectedSubgraph; import org.gephi.graph.impl.EdgeStore.EdgeInOutIterator; diff --git a/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 6d0d7da5..28b94e50 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -17,11 +17,11 @@ import it.unimi.dsi.fastutil.ints.IntRBTreeSet; import it.unimi.dsi.fastutil.ints.IntSortedSet; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedSubgraph; diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index f35a8312..f50449bb 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -52,6 +52,7 @@ import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.Instant; import java.time.ZoneId; import java.util.Date; import java.util.List; @@ -59,24 +60,13 @@ import java.util.Map; import java.util.Set; import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.GraphModel; -import org.gephi.graph.api.Origin; -import org.gephi.graph.api.Estimator; -import org.gephi.graph.api.TimeFormat; -import org.gephi.graph.api.types.TimestampBooleanMap; -import org.gephi.graph.api.types.TimestampByteMap; -import org.gephi.graph.api.types.TimestampCharMap; -import org.gephi.graph.api.types.TimestampDoubleMap; -import org.gephi.graph.api.types.TimestampFloatMap; -import org.gephi.graph.api.types.TimestampIntegerMap; -import org.gephi.graph.api.types.TimestampLongMap; -import org.gephi.graph.api.types.TimestampSet; -import org.gephi.graph.api.types.TimestampShortMap; -import org.gephi.graph.api.types.TimestampStringMap; -import org.gephi.graph.api.types.TimestampMap; import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Estimator; +import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; +import org.gephi.graph.api.Origin; +import org.gephi.graph.api.TimeFormat; import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalBooleanMap; import org.gephi.graph.api.types.IntervalByteMap; @@ -89,6 +79,17 @@ import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.IntervalShortMap; import org.gephi.graph.api.types.IntervalStringMap; +import org.gephi.graph.api.types.TimestampBooleanMap; +import org.gephi.graph.api.types.TimestampByteMap; +import org.gephi.graph.api.types.TimestampCharMap; +import org.gephi.graph.api.types.TimestampDoubleMap; +import org.gephi.graph.api.types.TimestampFloatMap; +import org.gephi.graph.api.types.TimestampIntegerMap; +import org.gephi.graph.api.types.TimestampLongMap; +import org.gephi.graph.api.types.TimestampMap; +import org.gephi.graph.api.types.TimestampSet; +import org.gephi.graph.api.types.TimestampShortMap; +import org.gephi.graph.api.types.TimestampStringMap; import org.gephi.graph.impl.EdgeImpl.EdgePropertiesImpl; import org.gephi.graph.impl.NodeImpl.NodePropertiesImpl; import org.gephi.graph.impl.utils.DataInputOutput; @@ -215,6 +216,7 @@ public class Serialization { final static int LIST = 229; final static int SET = 230; final static int MAP = 231; + final static int INSTANT = 232; // Store protected final Int2IntMap idMap; protected GraphModelImpl model; @@ -1030,6 +1032,17 @@ private IntervalIndexStore deserializeIntervalIndexStore(final DataInput is) thr return intervalIndexStore; } + private void serializeInstant(final DataOutput out, final Instant instant) throws IOException { + serialize(out, instant.getEpochSecond()); + serialize(out, instant.getNano()); + } + + private Instant deserializeInstant(final DataInput is) throws IOException, ClassNotFoundException { + long epochSecond = (long) deserialize(is); + int nano = (int) deserialize(is); + return Instant.ofEpochSecond(epochSecond, nano); + } + private void serializeGraphAttributes(final DataOutput out, final GraphAttributesImpl graphAttributes) throws IOException { serialize(out, graphAttributes.attributes.size()); for (Map.Entry entry : graphAttributes.attributes.entrySet()) { @@ -1625,6 +1638,10 @@ protected void serialize(final DataOutput out, final Object obj) throws IOExcept Map b = (Map) obj; out.write(MAP); serializeMap(out, b); + } else if (obj instanceof Instant) { + Instant i = (Instant) obj; + out.write(INSTANT); + serializeInstant(out, i); } else { throw new IOException("No serialization handler for this class: " + clazz.getName()); } @@ -2167,6 +2184,9 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce case MAP: ret = deserializeMap(is); break; + case INSTANT: + ret = deserializeInstant(is); + break; case -1: throw new EOFException(); diff --git a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java index 8ab48ef4..b3d63f6c 100644 --- a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java @@ -1,7 +1,6 @@ package org.gephi.graph.impl; import java.util.Iterator; -import java.util.function.Consumer; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Node; diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index d217dee8..c66ccb4e 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -21,13 +21,12 @@ import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Element; +import org.gephi.graph.api.Graph; import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Table; -import org.gephi.graph.api.TableLock; import org.gephi.graph.api.TableObserver; -import org.gephi.graph.api.Element; -import org.gephi.graph.api.Graph; public class TableImpl implements Collection, Table { diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java index e3aa2a00..3e8c50c9 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java @@ -15,14 +15,10 @@ */ package org.gephi.graph.impl; -import it.unimi.dsi.fastutil.objects.ObjectIterator; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Set; import org.gephi.graph.api.Element; import org.gephi.graph.api.ElementIterable; diff --git a/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java b/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java index 55bd083e..f9af6929 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/TimestampIndexStore.java @@ -16,9 +16,9 @@ package org.gephi.graph.impl; import it.unimi.dsi.fastutil.doubles.Double2IntRBTreeMap; -import org.gephi.graph.api.types.TimestampSet; import org.gephi.graph.api.Element; import org.gephi.graph.api.types.TimestampMap; +import org.gephi.graph.api.types.TimestampSet; public class TimestampIndexStore extends TimeIndexStore> { diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 13ea499a..55af3b70 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -20,13 +20,11 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphLock; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedGraph; import org.gephi.graph.api.UndirectedSubgraph; diff --git a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/api/AttributeUtilsTest.java similarity index 94% rename from src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java rename to src/test/java/org/gephi/graph/api/AttributeUtilsTest.java index 8e7cc69f..9812f50c 100644 --- a/src/test/java/org/gephi/graph/impl/AttributeUtilsTest.java +++ b/src/test/java/org/gephi/graph/api/AttributeUtilsTest.java @@ -13,13 +13,17 @@ * License for the specific language governing permissions and limitations under * the License. */ -package org.gephi.graph.impl; +package org.gephi.graph.api; import java.awt.Color; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -57,6 +61,7 @@ import org.gephi.graph.api.types.IntervalShortMap; import org.gephi.graph.api.types.IntervalStringMap; import org.gephi.graph.api.types.TimestampSet; +import org.gephi.graph.impl.TableImpl; import org.testng.Assert; import org.testng.annotations.Test; @@ -109,6 +114,18 @@ public void testParsePrimitiveTypes() { Assert.assertEquals(AttributeUtils.parse("0", boolean.class), false); } + @Test + public void testParseInstant() { + Assert.assertEquals(AttributeUtils.parse("2014-01-01T00:00:00Z", Instant.class), Instant + .parse("2014-01-01T00:00:00Z")); + Assert.assertEquals(AttributeUtils.parse("2014-01-01", Instant.class), Instant.parse("2014-01-01T00:00:00Z")); + } + + @Test(expectedExceptions = DateTimeParseException.class) + public void testParseInstantException() { + AttributeUtils.parse("foo", Instant.class); + } + @Test public void testParseArrayTypes() { Assert.assertEquals(AttributeUtils @@ -397,6 +414,7 @@ public void testIsSupported() { Assert.assertTrue(AttributeUtils.isSupported(HashSet.class)); Assert.assertTrue(AttributeUtils.isSupported(Map.class)); Assert.assertTrue(AttributeUtils.isSupported(HashMap.class)); + Assert.assertTrue(AttributeUtils.isSupported(Instant.class)); Assert.assertFalse(AttributeUtils.isSupported(Color.class)); Assert.assertFalse(AttributeUtils.isSupported(Collection.class)); @@ -644,6 +662,19 @@ public void testPrintDate() { // day } + @Test + public void testPrintDateWithInstant() { + String date = "2012-01-05"; + LocalDate localDate = LocalDate.of(2012, 1, 5); + ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.of("UTC")); + + Assert.assertEquals(AttributeUtils.printDate(zonedDateTime.toInstant(), ZoneId.of("UTC")), date); + Assert.assertEquals(AttributeUtils.printDate(zonedDateTime.toInstant(), null), date); + Assert.assertEquals(AttributeUtils.printDate(zonedDateTime.toInstant(), ZoneId.of("+00:30")), date); + Assert.assertEquals(AttributeUtils.printDate(zonedDateTime.toInstant(), ZoneId.of("+12:00")), date); + Assert.assertEquals(AttributeUtils.printDate(zonedDateTime.toInstant(), ZoneId.of("-00:30")), "2012-01-04"); + } + @Test public void testPrintDateTime() { String date = "2003-01-01T00:00:00.000-08:00"; @@ -664,6 +695,23 @@ public void testPrintDateTime() { .of("+12:00")), "2003-01-02T04:00:00.000+12:00"); } + @Test + public void testPrintDateTimeWithInstant() { + String date = "2012-01-05T08:00:00.000Z"; + LocalDate localDate = LocalDate.of(2012, 1, 5); + LocalTime localTime = LocalTime.of(0, 0, 0, 0); + ZonedDateTime zonedDateTime = ZonedDateTime.of(localDate, localTime, ZoneId.of("-08:00")); + + Assert.assertEquals(AttributeUtils.printDateTime(zonedDateTime.toInstant(), ZoneId.of("UTC")), date); + Assert.assertEquals(AttributeUtils.printDateTime(zonedDateTime.toInstant(), null), date); + Assert.assertEquals(AttributeUtils + .printDateTime(zonedDateTime.toInstant(), ZoneId.of("+00:30")), "2012-01-05T08:30:00.000+00:30"); + Assert.assertEquals(AttributeUtils + .printDateTime(zonedDateTime.toInstant(), ZoneId.of("+12:00")), "2012-01-05T20:00:00.000+12:00"); + Assert.assertEquals(AttributeUtils + .printDateTime(zonedDateTime.toInstant(), ZoneId.of("-12:00")), "2012-01-04T20:00:00.000-12:00"); + } + @Test public void testPrintArray() { Assert.assertEquals(AttributeUtils @@ -700,6 +748,8 @@ public void testPrint() { Assert.assertEquals(AttributeUtils.print(tm, TimeFormat.DATE, null), tm.toString(TimeFormat.DATE, null)); Assert.assertEquals(AttributeUtils.print(tm, TimeFormat.DATETIME, ZoneId.of("+00:30")), tm .toString(TimeFormat.DATETIME, ZoneId.of("+00:30"))); + + Assert.assertEquals(AttributeUtils.print(Instant.ofEpochMilli(0)), "1970-01-01T00:00:00Z"); } @Test @@ -936,6 +986,12 @@ public void testCopyTimestampSet() { assertCopyIsEqualsButNotSame(boolMap); } + @Test + public void testCopyInstant() { + Instant instant = Instant.now(); + Assert.assertSame(AttributeUtils.copy(instant), instant); + } + @Test public void testCopyList() { List list = new ArrayList(); diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index 2b058b0e..55bb1e9d 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -15,6 +15,7 @@ */ package org.gephi.graph.impl; +import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -103,6 +104,17 @@ public void testSetAttributeString() { Assert.assertEquals(node.getAttribute(column), 1); } + @Test + public void testSetAttributeInstant() { + GraphStore store = new GraphStore(); + Column column = generateBasicInstantColumn(store); + + Instant instant = Instant.parse("2014-01-01T00:00:00Z"); + NodeImpl node = new NodeImpl("0", store); + node.setAttribute("date", instant); + Assert.assertEquals(node.getAttribute(column), instant); + } + @Test public void testSetAttributeStandardizedType() { GraphStore store = new GraphStore(); @@ -1224,6 +1236,12 @@ private Column generateBasicBooleanColumn(GraphStore graphStore) { return graphStore.nodeTable.store.getColumn("visible"); } + private Column generateBasicInstantColumn(GraphStore graphStore) { + graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "date", Instant.class, "Date", null, + Origin.DATA, true, false)); + return graphStore.nodeTable.store.getColumn("date"); + } + private Column generateBasicListColumn(GraphStore graphStore) { graphStore.nodeTable.store.addColumn(new ColumnImpl(graphStore.nodeTable, "list", List.class, "List", null, Origin.DATA, true, false)); diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 7b50efca..c56c346f 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -45,6 +45,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; +import java.time.Instant; import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; @@ -652,6 +653,15 @@ public void testIntervalStringMap() throws IOException, ClassNotFoundException { Assert.assertEquals(timestampMap, l); } + @Test + public void testInstant() throws IOException, ClassNotFoundException { + Instant instant = Instant.ofEpochSecond(1234567890, 44553); + Serialization ser = new Serialization(null); + byte[] buf = ser.serialize(instant); + Instant l = (Instant) ser.deserialize(buf); + Assert.assertEquals(instant, l); + } + @Test public void testGraphAttributes() throws IOException, ClassNotFoundException { GraphAttributesImpl graphAttributes = new GraphAttributesImpl(); diff --git a/src/test/java/org/gephi/graph/impl/TableImplTest.java b/src/test/java/org/gephi/graph/impl/TableImplTest.java index ada6e5b4..5b7a1da4 100644 --- a/src/test/java/org/gephi/graph/impl/TableImplTest.java +++ b/src/test/java/org/gephi/graph/impl/TableImplTest.java @@ -16,6 +16,7 @@ package org.gephi.graph.impl; import java.awt.Color; +import java.time.Instant; import java.util.Arrays; import org.gephi.graph.api.Column; import org.gephi.graph.api.Origin; @@ -218,6 +219,13 @@ public void testStandardizeArrayDefaultValue() { Assert.assertEquals(d, new int[] { 1, 2 }); } + @Test + public void testInstantColumn() { + TableImpl table = new TableImpl<>(Node.class); + Column col = table.addColumn("test_col", Instant.class); + Assert.assertEquals(col.getTypeClass(), Instant.class); + } + @Test public void testRemoveColumn() { TableImpl table = new TableImpl<>(Node.class); From 14bb9d8007d7c3a847a2c363ee552021d7ffc53f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:03:12 +0200 Subject: [PATCH 138/271] Bump maven-source-plugin from 3.2.1 to 3.3.0 (#184) Bumps [maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.2.1 to 3.3.0. - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.2.1...maven-source-plugin-3.3.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d5c917aa..2b864079 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ org.apache.maven.plugins maven-source-plugin - 3.2.1 + 3.3.0 org.apache.maven.plugins From 28e277861b647627b791c1373a0b6b831ceab79a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:03:20 +0200 Subject: [PATCH 139/271] Bump testng from 7.7.1 to 7.8.0 (#183) Bumps [testng](https://github.com/testng-team/testng) from 7.7.1 to 7.8.0. - [Release notes](https://github.com/testng-team/testng/releases) - [Changelog](https://github.com/testng-team/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/testng-team/testng/compare/7.7.1...7.8.0) --- updated-dependencies: - dependency-name: org.testng:testng dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2b864079..d2525f56 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ org.testng testng - 7.7.1 + 7.8.0 test From 1798e4a286f2dd29e9709b79148c75f6039c7e72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:03:28 +0200 Subject: [PATCH 140/271] Bump build-helper-maven-plugin from 3.3.0 to 3.4.0 (#181) Bumps [build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/build-helper-maven-plugin-3.3.0...3.4.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d2525f56..776a44c3 100644 --- a/pom.xml +++ b/pom.xml @@ -133,7 +133,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.3.0 + 3.4.0 From 74fd2c3bc8a3ebaa108ee19edc22a58c4c9459fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:03:37 +0200 Subject: [PATCH 141/271] Bump maven-surefire-plugin from 3.0.0 to 3.1.0 (#180) Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.0.0...surefire-3.1.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 776a44c3..1de8ae2e 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0 + 3.1.0 org.apache.maven.plugins From ab72b152628cbb0059b36e63484e51fb7b094bcd Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 4 Jun 2023 14:59:06 +0200 Subject: [PATCH 142/271] Set version to 0.7.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1de8ae2e..8f99c350 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.7.0-SNAPSHOT + 0.7.0 jar GraphStore From 85dcb8ead4b61e8a68913e3cf5b250314151fcdb Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 4 Jun 2023 15:05:22 +0200 Subject: [PATCH 143/271] Set version to 0.7.1-SNAPSHOT --- README.md | 20 +++++++++++++++++++- pom.xml | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b3f32399..2df8d456 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,27 @@ API Documentation is available [here](https://www.javadoc.io/doc/org.gephi/graph Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) to get started. +## Usage + +### From a Maven project + +```xml + + org.gephi + graphstore + 0.7.0 + +``` + +### From a Gradle project + +``` +compile 'org.gephi:graphstore:0.7.0' +``` + ## Dependencies -GraphStore depends on FastUtil >= 6.0 and Colt 1.2.0. +GraphStore is built for JRE 11+ and depends on FastUtil and Colt. For a complete list of dependencies, consult the `pom.xml` file. diff --git a/pom.xml b/pom.xml index 8f99c350..0df2737b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.7.0 + 0.7.1-SNAPSHOT jar GraphStore From 7a54711e5ccf3b962a9e54b0ae3bf813ded576a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 20:15:08 +0200 Subject: [PATCH 144/271] Bump formatter-maven-plugin from 2.22.0 to 2.23.0 (#186) Bumps [formatter-maven-plugin](https://github.com/revelc/formatter-maven-plugin) from 2.22.0 to 2.23.0. - [Changelog](https://github.com/revelc/formatter-maven-plugin/blob/formatter-maven-plugin-2.23.0/CHANGELOG.md) - [Commits](https://github.com/revelc/formatter-maven-plugin/compare/formatter-maven-plugin-2.22.0...formatter-maven-plugin-2.23.0) --- updated-dependencies: - dependency-name: net.revelc.code.formatter:formatter-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0df2737b..f952bb06 100644 --- a/pom.xml +++ b/pom.xml @@ -128,7 +128,7 @@ net.revelc.code.formatter formatter-maven-plugin - 2.22.0 + 2.23.0 org.codehaus.mojo From 3950e29b096881849209d7eb95485621d927b683 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jun 2023 10:47:03 +0200 Subject: [PATCH 145/271] Bump maven-surefire-plugin from 3.1.0 to 3.1.2 (#187) Bumps [maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.0...surefire-3.1.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f952bb06..e6b4a5fd 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.1.0 + 3.1.2 org.apache.maven.plugins From 8fb287335a4b40051795147797f4e0df3f6efa16 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 12 Jun 2024 09:47:28 +0200 Subject: [PATCH 146/271] Add toString and diffAsString to Configuration (#199) --- .../org/gephi/graph/api/Configuration.java | 16 ++++ .../gephi/graph/impl/ConfigurationImpl.java | 79 +++++++++++++++++++ .../gephi/graph/impl/ConfigurationTest.java | 15 ++++ 3 files changed, 110 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/Configuration.java b/src/main/java/org/gephi/graph/api/Configuration.java index 0c24fd45..d74a1925 100644 --- a/src/main/java/org/gephi/graph/api/Configuration.java +++ b/src/main/java/org/gephi/graph/api/Configuration.java @@ -663,4 +663,20 @@ public boolean equals(Object o) { public int hashCode() { return delegate.hashCode(); } + + @Override + public String toString() { + return delegate.toString(); + } + + /** + * Returns a string representation of the differences between this configuration + * and another one. + * + * @param other the other configuration + * @return a string representation of the differences + */ + public String diffAsString(Configuration other) { + return delegate.diffAsString(other.delegate); + } } diff --git a/src/main/java/org/gephi/graph/impl/ConfigurationImpl.java b/src/main/java/org/gephi/graph/impl/ConfigurationImpl.java index 85785194..e89ae5ae 100644 --- a/src/main/java/org/gephi/graph/impl/ConfigurationImpl.java +++ b/src/main/java/org/gephi/graph/impl/ConfigurationImpl.java @@ -232,4 +232,83 @@ public int hashCode() { result = 31 * result + (isEnableParallelEdgesSameType() ? 1 : 0); return result; } + + @Override + public String toString() { + return "ConfigurationImpl{" + "nodeIdType:" + nodeIdType + ", edgeIdType:" + edgeIdType + ", edgeLabelType:" + edgeLabelType + ", edgeWeightType:" + edgeWeightType + ", timeRepresentation:" + timeRepresentation + ", edgeWeightColumn:" + edgeWeightColumn + ", enableAutoLocking:" + enableAutoLocking + ", enableAutoEdgeTypeRegistration:" + enableAutoEdgeTypeRegistration + ", enableIndexNodes:" + enableIndexNodes + ", enableIndexEdges:" + enableIndexEdges + ", enableIndexTime:" + enableIndexTime + ", enableObservers:" + enableObservers + ", enableNodeProperties:" + enableNodeProperties + ", enableEdgeProperties:" + enableEdgeProperties + ", enableSpatialIndex:" + enableSpatialIndex + ", enableParallelEdgesSameType:" + enableParallelEdgesSameType + '}'; + } + + public String diffAsString(ConfigurationImpl other) { + ConfigurationImpl otherImpl = (ConfigurationImpl) other; + StringBuilder sb = new StringBuilder(); + if (!getNodeIdType().equals(otherImpl.getNodeIdType())) { + sb.append("nodeIdType: ").append(getNodeIdType()).append(" != ").append(otherImpl.getNodeIdType()) + .append("\n"); + } + if (!getEdgeIdType().equals(otherImpl.getEdgeIdType())) { + sb.append("edgeIdType: ").append(getEdgeIdType()).append(" != ").append(otherImpl.getEdgeIdType()) + .append("\n"); + } + if (!getEdgeLabelType().equals(otherImpl.getEdgeLabelType())) { + sb.append("edgeLabelType: ").append(getEdgeLabelType()).append(" != ").append(otherImpl.getEdgeLabelType()) + .append("\n"); + } + if (!getEdgeWeightType().equals(otherImpl.getEdgeWeightType())) { + sb.append("edgeWeightType: ").append(getEdgeWeightType()).append(" != ") + .append(otherImpl.getEdgeWeightType()).append("\n"); + } + if (getTimeRepresentation() != otherImpl.getTimeRepresentation()) { + sb.append("timeRepresentation: ").append(getTimeRepresentation()).append(" != ") + .append(otherImpl.getTimeRepresentation()).append("\n"); + } + if (isEdgeWeightColumn() != otherImpl.isEdgeWeightColumn()) { + sb.append("edgeWeightColumn: ").append(isEdgeWeightColumn()).append(" != ") + .append(otherImpl.isEdgeWeightColumn()).append("\n"); + } + if (isEnableAutoLocking() != otherImpl.isEnableAutoLocking()) { + sb.append("enableAutoLocking: ").append(isEnableAutoLocking()).append(" != ") + .append(otherImpl.isEnableAutoLocking()).append("\n"); + } + if (isEnableAutoEdgeTypeRegistration() != otherImpl.isEnableAutoEdgeTypeRegistration()) { + sb.append("enableAutoEdgeTypeRegistration: ").append(isEnableAutoEdgeTypeRegistration()).append(" != ") + .append(otherImpl.isEnableAutoEdgeTypeRegistration()).append("\n"); + } + if (isEnableIndexNodes() != otherImpl.isEnableIndexNodes()) { + sb.append("enableIndexNodes: ").append(isEnableIndexNodes()).append(" != ") + .append(otherImpl.isEnableIndexNodes()).append("\n"); + } + if (isEnableIndexEdges() != otherImpl.isEnableIndexEdges()) { + sb.append("enableIndexEdges: ").append(isEnableIndexEdges()).append(" != ") + .append(otherImpl.isEnableIndexEdges()).append("\n"); + } + if (isEnableIndexTime() != otherImpl.isEnableIndexTime()) { + sb.append("enableIndexTime: ").append(isEnableIndexTime()).append(" != ") + .append(otherImpl.isEnableIndexTime()).append("\n"); + } + if (isEnableObservers() != otherImpl.isEnableObservers()) { + sb.append("enableObservers: ").append(isEnableObservers()).append(" != ") + .append(otherImpl.isEnableObservers()).append("\n"); + } + if (isEnableNodeProperties() != otherImpl.isEnableNodeProperties()) { + sb.append("enableNodeProperties: ").append(isEnableNodeProperties()).append(" != ") + .append(otherImpl.isEnableNodeProperties()).append("\n"); + } + if (isEnableEdgeProperties() != otherImpl.isEnableEdgeProperties()) { + sb.append("enableEdgeProperties: ").append(isEnableEdgeProperties()).append(" != ") + .append(otherImpl.isEnableEdgeProperties()).append("\n"); + } + if (isEnableSpatialIndex() != otherImpl.isEnableSpatialIndex()) { + sb.append("enableSpatialIndex: ").append(isEnableSpatialIndex()).append(" != ") + .append(otherImpl.isEnableSpatialIndex()).append("\n"); + } + if (isEnableParallelEdgesSameType() != otherImpl.isEnableParallelEdgesSameType()) { + sb.append("enableParallelEdgesSameType: ").append(isEnableParallelEdgesSameType()).append(" != ") + .append(otherImpl.isEnableParallelEdgesSameType()).append("\n"); + } + // Remove last /n + if (sb.length() > 0) { + sb.setLength(sb.length() - 1); + } + return sb.toString(); + } } diff --git a/src/test/java/org/gephi/graph/impl/ConfigurationTest.java b/src/test/java/org/gephi/graph/impl/ConfigurationTest.java index 1b4296df..9222ed69 100644 --- a/src/test/java/org/gephi/graph/impl/ConfigurationTest.java +++ b/src/test/java/org/gephi/graph/impl/ConfigurationTest.java @@ -284,4 +284,19 @@ public void testToConfiguration() { public void testExceptionSpatialIndexWithDisabledNodeProperties() { Configuration.builder().enableSpatialIndex(true).enableNodeProperties(false).build(); } + + @Test + public void testToSting() { + Configuration c = Configuration.builder().build(); + Assert.assertNotNull(c.toString()); + Assert.assertTrue(c.toString().contains(c.getTimeRepresentation().name())); + } + + @Test + public void testDiffAsString() { + Configuration c1 = Configuration.builder().build(); + Configuration c2 = Configuration.builder().nodeIdType(Float.class).build(); + Assert.assertNotNull(c1.diffAsString(c2)); + Assert.assertEquals(c1.diffAsString(c2), "nodeIdType: class java.lang.String != class java.lang.Float"); + } } From 68fd7e1aa1f1e65a41a85d124f2accdedf6c08eb Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 12 Jun 2024 09:47:43 +0200 Subject: [PATCH 147/271] Add getNodeByStoreId and getEdgeByStoreId methods to Graph (#198) * Add getNodeByStoreId and getEdgeByStoreId methods to Graph * Formatting --- .../org/gephi/graph/api/Configuration.java | 18 +++++----- src/main/java/org/gephi/graph/api/Graph.java | 16 +++++++++ .../java/org/gephi/graph/api/GraphModel.java | 4 +-- .../org/gephi/graph/api/SpatialIndex.java | 2 +- .../java/org/gephi/graph/api/package.html | 10 +++--- .../org/gephi/graph/api/types/package.html | 8 ++--- .../java/org/gephi/graph/impl/EdgeStore.java | 8 +++++ .../java/org/gephi/graph/impl/GraphStore.java | 20 +++++++++++ .../gephi/graph/impl/GraphViewDecorator.java | 28 +++++++++++++++ .../java/org/gephi/graph/impl/NodeStore.java | 8 +++++ .../org/gephi/graph/impl/NodesQuadTree.java | 2 +- .../gephi/graph/impl/UndirectedDecorator.java | 10 ++++++ .../java/org/gephi/graph/spi/package.html | 8 ++--- .../org/gephi/graph/impl/BasicGraphStore.java | 9 +++++ .../org/gephi/graph/impl/GraphStoreTest.java | 35 +++++++++++++++++++ 15 files changed, 160 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/Configuration.java b/src/main/java/org/gephi/graph/api/Configuration.java index d74a1925..70c00675 100644 --- a/src/main/java/org/gephi/graph/api/Configuration.java +++ b/src/main/java/org/gephi/graph/api/Configuration.java @@ -27,7 +27,7 @@ * create a GraphModel with custom configuration. *

* Create instances by using the builder: - * + * *

  * Configuration config = Configuration.builder().build();
  * 
@@ -260,7 +260,7 @@ public boolean isEnableObservers() { * for each type. *

* Default is true. - * + * * @param enableAutoEdgeTypeRegistration enable auto edge type registration * @return this builder */ @@ -281,7 +281,7 @@ public boolean isEnableAutoEdgeTypeRegistration() { * properties aren't needed, disabling them can save memory. *

* Default is true. - * + * * @param enableNodeProperties enable node properties * @return this builder */ @@ -302,7 +302,7 @@ public boolean isEnableNodeProperties() { * properties aren't needed, disabling them can save memory. *

* Default is true. - * + * * @param enableEdgeProperties enable edge properties * @return this builder */ @@ -325,7 +325,7 @@ public boolean isEnableEdgeProperties() { * The spatial index can be retrieved from {@link GraphModel#getSpatialIndex()}. *

* Default is false. - * + * * @param enableSpatialIndex enable edge properties * @return this builder */ @@ -349,7 +349,7 @@ public boolean isEnableSpatialIndex() { * return results. *

* Default is true. - * + * * @param enableIndexNodes enable node attribute indexing * @return this builder */ @@ -373,7 +373,7 @@ public boolean isEnableIndexNodes() { * return results. *

* Default is true. - * + * * @param enableIndexEdges enable edge attribute indexing * @return this builder */ @@ -416,7 +416,7 @@ public boolean isEnableIndexTime() { * If disabled, only a single edge of a given type can exist between two nodes. *

* Default is false. - * + * * @param enableParallelEdgesSameType enable parallel edges of the same type * @return this builder */ @@ -586,7 +586,7 @@ public Boolean getEdgeWeightColumn() { /** * Sets whether to create an edge weight column. *

- * + * * @deprecated Use {@link #builder()} instead. * * @param edgeWeightColumn edge weight column diff --git a/src/main/java/org/gephi/graph/api/Graph.java b/src/main/java/org/gephi/graph/api/Graph.java index ad56cc8d..10ad9b24 100644 --- a/src/main/java/org/gephi/graph/api/Graph.java +++ b/src/main/java/org/gephi/graph/api/Graph.java @@ -131,6 +131,14 @@ public interface Graph { */ public Node getNode(Object id); + /** + * Gets a node given its store id. + * + * @param storeId the store id + * @return the node, or null if not found + */ + public Node getNodeByStoreId(int storeId); + /** * Returns true if a node with id as identifier exists. * @@ -147,6 +155,14 @@ public interface Graph { */ public Edge getEdge(Object id); + /** + * Gets an edge given its store id. + * + * @param storeId the store id + * @return the edge, or null if not found + */ + public Edge getEdgeByStoreId(int storeId); + /** * Returns true if an edge with id as identifier exists. * diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index 0e138a1c..ba545069 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -38,7 +38,7 @@ * *

* New instances can be obtained via the embedded factory: - * + * *

  * GraphModel model = GraphModel.Factory.newInstance();
  * 
@@ -49,7 +49,7 @@ * Configuration configuration = Configuration.builder().build(); * GraphModel model = GraphModel.Factory.newInstance(configuration); * - * + * * This API revolves around a set of simple concepts. A GraphModel * encapsulate all elements and metadata associated with a graph structure. In * other words it's a single graph, but it also contains configuration, indices, diff --git a/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java index 633a1356..882852ec 100644 --- a/src/main/java/org/gephi/graph/api/SpatialIndex.java +++ b/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -17,7 +17,7 @@ /** * Object to query the nodes and edges of the graph in a spatial context. - * + * * @author Eduardo Ramos */ public interface SpatialIndex { diff --git a/src/main/java/org/gephi/graph/api/package.html b/src/main/java/org/gephi/graph/api/package.html index 172830cd..ae5d4a6f 100644 --- a/src/main/java/org/gephi/graph/api/package.html +++ b/src/main/java/org/gephi/graph/api/package.html @@ -1,8 +1,8 @@ - - + + - Complete API description, where + Complete API description, where GraphModel - is the entry point. + is the entry point. - \ No newline at end of file + diff --git a/src/main/java/org/gephi/graph/api/types/package.html b/src/main/java/org/gephi/graph/api/types/package.html index dba9d896..ca0a00dd 100644 --- a/src/main/java/org/gephi/graph/api/types/package.html +++ b/src/main/java/org/gephi/graph/api/types/package.html @@ -1,6 +1,6 @@ - - + + - Custom types the API supports, in addition of primitive and arrays. + Custom types the API supports, in addition of primitive and arrays. - \ No newline at end of file + diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index d60cc48f..7328bb01 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -448,6 +448,14 @@ public EdgeImpl get(int id) { return blocks[id / GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE].get(id); } + // Only used for Graph.getEdgeByStoreId + public EdgeImpl getForGetByStoreId(int id) { + if (id < 0 || !isValidIndex(id)) { + return null; + } + return blocks[id / GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE].get(id); + } + public EdgeImpl get(final Object id) { checkNonNullObject(id); diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 3038d5c4..3d6a114f 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -197,6 +197,16 @@ public NodeImpl getNode(final Object id) { } } + @Override + public NodeImpl getNodeByStoreId(final int id) { + autoReadLock(); + try { + return nodeStore.getForGetByStoreId(id); + } finally { + autoReadUnlock(); + } + } + @Override public boolean hasNode(final Object id) { return getNode(id) != null; @@ -212,6 +222,16 @@ public EdgeImpl getEdge(final Object id) { } } + @Override + public EdgeImpl getEdgeByStoreId(final int id) { + autoReadLock(); + try { + return edgeStore.getForGetByStoreId(id); + } finally { + autoReadUnlock(); + } + } + @Override public boolean hasEdge(final Object id) { return getEdge(id) != null; diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 76823d09..266ffa7e 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -318,6 +318,20 @@ public Node getNode(Object id) { } } + @Override + public Node getNodeByStoreId(int id) { + graphStore.autoReadLock(); + try { + NodeImpl node = graphStore.getNodeByStoreId(id); + if (node != null && view.containsNode(node)) { + return node; + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + @Override public boolean hasNode(final Object id) { return getNode(id) != null; @@ -337,6 +351,20 @@ public Edge getEdge(Object id) { } } + @Override + public Edge getEdgeByStoreId(int id) { + graphStore.autoReadLock(); + try { + EdgeImpl edge = graphStore.getEdgeByStoreId(id); + if (edge != null && view.containsEdge(edge)) { + return edge; + } + return null; + } finally { + graphStore.autoReadUnlock(); + } + } + @Override public boolean hasEdge(final Object id) { return getEdge(id) != null; diff --git a/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java index 9c2c8add..709deb31 100644 --- a/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -125,6 +125,14 @@ public NodeImpl get(final int id) { return blocks[id / GraphStoreConfiguration.NODESTORE_BLOCK_SIZE].get(id); } + // Only used for Graph.getNodeByStoreId + public NodeImpl getForGetByStoreId(int id) { + if (id < 0 || !isValidIndex(id)) { + return null; + } + return blocks[id / GraphStoreConfiguration.NODESTORE_BLOCK_SIZE].get(id); + } + public NodeImpl get(final Object id) { int index = dictionary.getInt(id); if (index != NodeStore.NULL_ID) { diff --git a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index a31746ef..c32de592 100644 --- a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -17,7 +17,7 @@ * Adapted from https://bitbucket.org/C3/quadtree/wiki/Home * * TODO: unit tests!! - * + * * @author Eduardo Ramos */ public class NodesQuadTree { diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 55af3b70..92f60a41 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -110,6 +110,11 @@ public Node getNode(Object id) { return store.getNode(id); } + @Override + public Node getNodeByStoreId(int id) { + return store.getNodeByStoreId(id); + } + @Override public boolean hasNode(final Object id) { return store.hasNode(id); @@ -120,6 +125,11 @@ public Edge getEdge(Object id) { return store.getEdge(id); } + @Override + public Edge getEdgeByStoreId(int storeId) { + return store.getEdgeByStoreId(storeId); + } + @Override public boolean hasEdge(final Object id) { return store.hasEdge(id); diff --git a/src/main/java/org/gephi/graph/spi/package.html b/src/main/java/org/gephi/graph/spi/package.html index b65b2e11..39474ac9 100644 --- a/src/main/java/org/gephi/graph/spi/package.html +++ b/src/main/java/org/gephi/graph/spi/package.html @@ -1,6 +1,6 @@ - - + + - SPI interfaces clients can implement to extend the API. + SPI interfaces clients can implement to extend the API. - \ No newline at end of file + diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index ddd0fc68..d4dffa29 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -209,6 +209,10 @@ public Node getNode(Object id) { return nodeStore.get(id); } + public Node getNodeByStoreId(int storeId) { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public boolean hasNode(Object id) { return nodeStore.get(id) != null; @@ -219,6 +223,11 @@ public Edge getEdge(Object id) { return edgeStore.get(id); } + @Override + public Edge getEdgeByStoreId(int storeId) { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override public boolean hasEdge(Object id) { return edgeStore.get(id) != null; diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index ec6fb2e4..9d4c70ab 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -620,6 +620,24 @@ public void testGetNode() { Assert.assertFalse(graphStore.hasNode("bar")); } + @Test + public void testGetNodeByStoreId() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + for (Node node : graphStore.getNodes().toArray()) { + Assert.assertNotNull(graphStore.getNodeByStoreId(node.getStoreId())); + } + } + + @Test + public void testGetNodeByStoreIdIsNull() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + for (Node node : graphStore.getNodes().toArray()) { + int storeId = node.getStoreId(); + graphStore.removeNode(node); + Assert.assertNull(graphStore.getNodeByStoreId(storeId)); + } + } + @Test public void testGetEdge() { GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); @@ -629,6 +647,23 @@ public void testGetEdge() { Assert.assertFalse(graphStore.hasEdge("bar")); } + @Test + public void testGetEdgeByStoreId() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + for (Edge edge : graphStore.getEdges().toArray()) { + Assert.assertNotNull(graphStore.getEdgeByStoreId(edge.getStoreId())); + } + } + + @Test + public void testGetEdgeByStoreIdIsNull() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + Edge toRemove = graphStore.getEdge("0"); + int storeId = toRemove.getStoreId(); + graphStore.removeEdge(toRemove); + Assert.assertNull(graphStore.getEdgeByStoreId(storeId)); + } + @Test public void testGetMutualEdge() { GraphStore graphStore = new GraphStore(); From 992b0823baa30e873cc089f9d8018bca703a3ad4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 12 Jun 2024 10:11:50 +0200 Subject: [PATCH 148/271] Upgrade dependencies (#200) --- pom.xml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index e6b4a5fd..91b941e6 100644 --- a/pom.xml +++ b/pom.xml @@ -59,13 +59,13 @@ org.testng testng - 7.8.0 + 7.10.2 test it.unimi.dsi fastutil - 8.5.12 + 8.5.13 colt @@ -80,37 +80,37 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.13.0 org.apache.maven.plugins maven-surefire-plugin - 3.1.2 + 3.2.5 org.apache.maven.plugins maven-source-plugin - 3.3.0 + 3.3.1 org.apache.maven.plugins maven-javadoc-plugin - 3.5.0 + 3.7.0 org.apache.maven.plugins maven-gpg-plugin - 3.1.0 + 3.2.4 org.sonatype.plugins nexus-staging-maven-plugin - 1.6.13 + 1.7.0 org.jacoco jacoco-maven-plugin - 0.8.10 + 0.8.12 org.eluder.coveralls @@ -133,7 +133,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.4.0 + 3.6.0 From b7238992365997bb26c4990b357ca13b6421bc1d Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 12 Jun 2024 10:15:18 +0200 Subject: [PATCH 149/271] Change to use sonatype user tokens instead --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a35553d..eb0a1ee5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: - name: Publish package run: mvn -B -Djava.awt.headless=true deploy -P release env: - OSSRH_USER: ${{ secrets.OSSRH_USER }} - OSSRH_PASS: ${{ secrets.OSSRH_PASS }} + OSSRH_USER: ${{ secrets.OSSRH_TOKEN_USER }} + OSSRH_PASS: ${{ secrets.OSSRH_TOKEN_PASSWD }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Submit test coverage to Coveralls run: mvn test jacoco:report coveralls:report -DrepoToken=${{ secrets.COVERALLS_TOKEN }} From 6ed5e9c1d41c32d9f008ba1c2195fb84f6bc2d1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 10:16:03 +0200 Subject: [PATCH 150/271] Bump actions/checkout from 3 to 4 (#188) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb0a1ee5..5c4d544b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Maven Central Repository uses: actions/setup-java@v3 with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 96347f8d..88429ce6 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -7,7 +7,7 @@ jobs: build_and_test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 11 uses: actions/setup-java@v3 with: From 5758c751ec3aac5339405296eec52f6c7663151a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 12 Jun 2024 10:20:17 +0200 Subject: [PATCH 151/271] Release version 0.7.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 91b941e6..92007652 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.7.1-SNAPSHOT + 0.7.1 jar GraphStore From e91bcd6a15d1c0ea6b1f527e53418390e1d8e36a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 12 Jun 2024 10:38:21 +0200 Subject: [PATCH 152/271] Set version to 0.7.2-SNAPSHOT --- README.md | 4 ++-- pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2df8d456..87be9cfd 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.7.0 + 0.7.1 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.7.0' +compile 'org.gephi:graphstore:0.7.1' ``` ## Dependencies diff --git a/pom.xml b/pom.xml index 92007652..bb341988 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.7.1 + 0.7.2-SNAPSHOT jar GraphStore From 1b355eda3c760944202e24b4a56e212447b6a056 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 12 Jun 2024 14:26:40 +0200 Subject: [PATCH 153/271] Spatial index in graph view (#202) * Move spatial index to Graph so it can be used in graph views * Fix * Add documentation --------- Co-authored-by: Eduardo Ramos --- .../org/gephi/graph/api/Configuration.java | 2 +- src/main/java/org/gephi/graph/api/Graph.java | 7 +++ .../java/org/gephi/graph/api/GraphModel.java | 7 --- src/main/java/org/gephi/graph/api/Rect2D.java | 55 ++++++++++++++++++- .../org/gephi/graph/api/SpatialIndex.java | 12 ++++ .../org/gephi/graph/impl/GraphModelImpl.java | 8 --- .../java/org/gephi/graph/impl/GraphStore.java | 27 ++++----- .../gephi/graph/impl/GraphViewDecorator.java | 14 +++++ .../gephi/graph/impl/UndirectedDecorator.java | 18 +++--- .../org/gephi/graph/impl/BasicGraphStore.java | 5 ++ 10 files changed, 110 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/Configuration.java b/src/main/java/org/gephi/graph/api/Configuration.java index 70c00675..3b14ba87 100644 --- a/src/main/java/org/gephi/graph/api/Configuration.java +++ b/src/main/java/org/gephi/graph/api/Configuration.java @@ -322,7 +322,7 @@ public boolean isEnableEdgeProperties() { * If enabled, the spatial index is updated while node positions are updated. If * unused, disabling it is recommended as it adds some overhead. *

- * The spatial index can be retrieved from {@link GraphModel#getSpatialIndex()}. + * The spatial index can be retrieved from {@link Graph#getSpatialIndex()}. *

* Default is false. * diff --git a/src/main/java/org/gephi/graph/api/Graph.java b/src/main/java/org/gephi/graph/api/Graph.java index 10ad9b24..921dba72 100644 --- a/src/main/java/org/gephi/graph/api/Graph.java +++ b/src/main/java/org/gephi/graph/api/Graph.java @@ -557,4 +557,11 @@ public interface Graph { * @return graph lock */ GraphLock getLock(); + + /** + * Returns the spatial index. + * + * @return spatial index + */ + SpatialIndex getSpatialIndex(); } diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index ba545069..64383bbd 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -683,13 +683,6 @@ public static interface DefaultColumns { */ public GraphObserver createGraphObserver(Graph graph, boolean withGraphDiff); - /** - * Returns the spatial index. - * - * @return spatial index - */ - public SpatialIndex getSpatialIndex(); - /** * Returns the time format used to display time. * diff --git a/src/main/java/org/gephi/graph/api/Rect2D.java b/src/main/java/org/gephi/graph/api/Rect2D.java index 53f909ad..bc2b34fb 100644 --- a/src/main/java/org/gephi/graph/api/Rect2D.java +++ b/src/main/java/org/gephi/graph/api/Rect2D.java @@ -66,18 +66,39 @@ public Rect2D(float minX, float minY, float maxX, float maxY) { this.maxY = maxY; } + /** + * Return the rectangle's width. + * + * @return the rectangle's width + */ public float width() { return maxX - minX; } + /** + * Return the rectangle's height. + * + * @return the rectangle's height + */ public float height() { return maxY - minY; } + /** + * Return the rectangle's center, as an array where the first element is the x + * coordinate and the second element is the y coordinate. + * + * @return the rectangle's center + */ public float[] center() { return new float[] { (maxX + minX) / 2, (maxY + minY) / 2 }; } + /** + * Return the rectangle's radius. + * + * @return the rectangle's radius + */ public float radius() { float width = width(); float height = height(); @@ -92,11 +113,17 @@ public String toString() { return toString(FORMAT); } - public String toString(NumberFormat formatter) { + private String toString(NumberFormat formatter) { return "(" + formatter.format(minX) + " " + formatter.format(minY) + ") < " + "(" + formatter .format(maxX) + " " + formatter.format(maxY) + ")"; } + /** + * Returns true if this rectangle contains the given rectangle. + * + * @param rect the rectangle to check + * @return true if this rectangle contains, false otherwise + */ public boolean contains(Rect2D rect) { if (rect == this) { return true; @@ -105,6 +132,12 @@ public boolean contains(Rect2D rect) { return contains(rect.minX, rect.minY, rect.maxX, rect.maxY); } + /** + * Returns true if this rectangle intersects the given rectangle. + * + * @param rect the rectangle to check + * @return true if this rectangle intersects, false otherwise + */ public boolean intersects(Rect2D rect) { if (rect == this) { return true; @@ -113,10 +146,30 @@ public boolean intersects(Rect2D rect) { return intersects(rect.minX, rect.minY, rect.maxX, rect.maxY); } + /** + * Returns true if this rectangle contains the given rectangle. + * + * @param minX the x coordinate of the minimum corner + * @param minY the y coordinate of the minimum corner + * @param maxX the x coordinate of the maximum corner + * @param maxY the y coordinate of the maximum corner + * + * @return true if this rectangle contains, false otherwise + */ public boolean contains(float minX, float minY, float maxX, float maxY) { return this.minX <= minX && this.minY <= minY && this.maxX >= maxX && this.maxY >= maxY; } + /** + * Returns true if this rectangle intersects the given rectangle. + * + * @param minX the x coordinate of the minimum corner + * @param minY the y coordinate of the minimum corner + * @param maxX the x coordinate of the maximum corner + * @param maxY the y coordinate of the maximum corner + * + * @return true if this rectangle intersects, false otherwise + */ public boolean intersects(float minX, float minY, float maxX, float maxY) { return this.minX <= maxX && minX <= this.maxX && this.maxY >= minY && maxY >= this.minY; } diff --git a/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java index 882852ec..4efb291e 100644 --- a/src/main/java/org/gephi/graph/api/SpatialIndex.java +++ b/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -22,7 +22,19 @@ */ public interface SpatialIndex { + /** + * Returns the nodes in the given area. + * + * @param rect area to query + * @return nodes in the area + */ NodeIterable getNodesInArea(Rect2D rect); + /** + * Returns the edges in the given area. + * + * @param rect area to query + * @return edges in the area + */ EdgeIterable getEdgesInArea(Rect2D rect); } diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 2304493e..062fba91 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -369,14 +369,6 @@ public TimeIndex getEdgeTimeIndex(GraphView view) { return null; } - @Override - public SpatialIndex getSpatialIndex() { - if (!configuration.isEnableSpatialIndex()) { - throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); - } - return store.spatialIndex; - } - @Override public GraphObserver createGraphObserver(Graph graph, boolean withGraphDiff) { store.autoWriteLock(); diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 3d6a114f..3d8b015c 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -23,23 +23,8 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.DirectedGraph; -import org.gephi.graph.api.DirectedSubgraph; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.EdgeIterable; -import org.gephi.graph.api.ElementIterable; -import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphModel; -import org.gephi.graph.api.GraphView; -import org.gephi.graph.api.Interval; -import org.gephi.graph.api.Node; -import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.Origin; -import org.gephi.graph.api.Subgraph; -import org.gephi.graph.api.Table; -import org.gephi.graph.api.TimeFormat; -import org.gephi.graph.api.TimeRepresentation; + +import org.gephi.graph.api.*; import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.TimestampSet; @@ -721,6 +706,14 @@ public GraphLockImpl getLock() { return lock; } + @Override + public SpatialIndex getSpatialIndex() { + if (spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return spatialIndex; + } + protected void autoReadLock() { if (configuration.isEnableAutoLocking()) { readLock(); diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 266ffa7e..db87b820 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -752,6 +752,14 @@ public Graph getRootGraph() { return graphStore; } + @Override + public SpatialIndex getSpatialIndex() { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return this; + } + void checkWriteLock() { if (graphStore.lock != null) { graphStore.lock.checkHoldWriteLock(); @@ -821,12 +829,18 @@ boolean isUndirectedToIgnore(final EdgeImpl edge) { @Override public NodeIterable getNodesInArea(Rect2D rect) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } Iterator iterator = graphStore.spatialIndex.getNodesInArea(rect).iterator(); return new NodeIterableWrapper(new NodeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); } @Override public EdgeIterable getEdgesInArea(Rect2D rect) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } Iterator iterator = graphStore.spatialIndex.getEdgesInArea(rect).iterator(); return new EdgeIterableWrapper(new EdgeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); } diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 92f60a41..91bc601e 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -17,17 +17,8 @@ import java.util.Collection; import java.util.Set; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.EdgeIterable; -import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphModel; -import org.gephi.graph.api.GraphView; -import org.gephi.graph.api.Interval; -import org.gephi.graph.api.Node; -import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.Subgraph; -import org.gephi.graph.api.UndirectedGraph; -import org.gephi.graph.api.UndirectedSubgraph; + +import org.gephi.graph.api.*; public class UndirectedDecorator implements UndirectedGraph, UndirectedSubgraph { @@ -422,4 +413,9 @@ public void not() { public Graph getRootGraph() { return this; } + + @Override + public SpatialIndex getSpatialIndex() { + return store.getSpatialIndex(); + } } diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index d4dffa29..17b45f9c 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -1712,4 +1712,9 @@ public void doBreak() { // Not used because no locking } } + + @Override + public SpatialIndex getSpatialIndex() { + return null; + } } From 2f140ec1f1caf360a45287ee2da96369db9d71fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 23 Jun 2024 07:54:46 +0200 Subject: [PATCH 154/271] Bump actions/setup-java from 3 to 4 (#203) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-java dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c4d544b..b303a873 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Maven Central Repository - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 88429ce6..cddbc77e 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK 11 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '11' distribution: 'temurin' From 709c26874c7e673534ebac573da7e7e3db18a78a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 23 Jun 2024 07:58:42 +0200 Subject: [PATCH 155/271] Set version to 0.7.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index bb341988..26993116 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.7.2-SNAPSHOT + 0.7.2 jar GraphStore From bc099d18b0fed34f24bd339da79cf9f10b9bc856 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 23 Jun 2024 08:08:45 +0200 Subject: [PATCH 156/271] Set version to 0.7.3-SNAPSHOT --- README.md | 4 ++-- pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 87be9cfd..50ba7f56 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.7.1 + 0.7.2 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.7.1' +compile 'org.gephi:graphstore:0.7.2' ``` ## Dependencies diff --git a/pom.xml b/pom.xml index 26993116..06edd769 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.7.2 + 0.7.3-SNAPSHOT jar GraphStore From 90337c69336d5c2bd29a3e776840c95e6d1774dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 11:56:58 +0200 Subject: [PATCH 157/271] Bump org.apache.maven.plugins:maven-surefire-plugin from 3.2.5 to 3.4.0 (#210) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.2.5 to 3.4.0. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.2.5...surefire-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 06edd769..da7fe3b2 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.5 + 3.4.0 org.apache.maven.plugins From f3c064e3a04fcd3ef3708dc62d7c58bb9c01c54f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 11:57:10 +0200 Subject: [PATCH 158/271] Bump it.unimi.dsi:fastutil from 8.5.13 to 8.5.14 (#208) Bumps [it.unimi.dsi:fastutil](https://github.com/vigna/fastutil) from 8.5.13 to 8.5.14. - [Changelog](https://github.com/vigna/fastutil/blob/master/CHANGES) - [Commits](https://github.com/vigna/fastutil/commits/8.5.14) --- updated-dependencies: - dependency-name: it.unimi.dsi:fastutil dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index da7fe3b2..7d0db9ff 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ it.unimi.dsi fastutil - 8.5.13 + 8.5.14 colt From 5db260e19e40d31b616f83236e02ef28faca5e37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 11:57:18 +0200 Subject: [PATCH 159/271] Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.7.0 to 3.8.0 (#207) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.7.0 to 3.8.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.7.0...maven-javadoc-plugin-3.8.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7d0db9ff..261fa78e 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.7.0 + 3.8.0 org.apache.maven.plugins From a29241f8990c40645cc8030668822b7e86f96976 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Aug 2024 11:57:29 +0200 Subject: [PATCH 160/271] Bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.4 to 3.2.5 (#209) Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.4 to 3.2.5. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.4...maven-gpg-plugin-3.2.5) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 261fa78e..7a01377a 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.4 + 3.2.5 org.sonatype.plugins From e55001d770a3c0c60bf446642e00e27f5edf067e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 09:38:35 +0200 Subject: [PATCH 161/271] Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.8.0 to 3.11.1 (#219) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.8.0 to 3.11.1. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.8.0...maven-javadoc-plugin-3.11.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7a01377a..6d6e8753 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.8.0 + 3.11.1 org.apache.maven.plugins From 4fa41f8330d96f5489c1d2f34cb0cfa350494cde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 09:38:47 +0200 Subject: [PATCH 162/271] Bump org.apache.maven.plugins:maven-surefire-plugin from 3.4.0 to 3.5.2 (#218) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.4.0 to 3.5.2. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.4.0...surefire-3.5.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6d6e8753..e95cf50f 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.4.0 + 3.5.2 org.apache.maven.plugins From 0df21b724efd0a1abebb31420a4797597ad783d2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 09:38:56 +0200 Subject: [PATCH 163/271] Bump it.unimi.dsi:fastutil from 8.5.14 to 8.5.15 (#217) Bumps [it.unimi.dsi:fastutil](https://github.com/vigna/fastutil) from 8.5.14 to 8.5.15. - [Changelog](https://github.com/vigna/fastutil/blob/master/CHANGES) - [Commits](https://github.com/vigna/fastutil/compare/8.5.14...8.5.15) --- updated-dependencies: - dependency-name: it.unimi.dsi:fastutil dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e95cf50f..6b927824 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ it.unimi.dsi fastutil - 8.5.14 + 8.5.15 colt From c1e8c018266ff3097089520976a51bce40b5d1d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 09:39:05 +0200 Subject: [PATCH 164/271] Bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.5 to 3.2.7 (#214) Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.5 to 3.2.7. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.5...maven-gpg-plugin-3.2.7) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6b927824..c3e3be72 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.5 + 3.2.7 org.sonatype.plugins From 7e1b2fe19976550338d78cb37a61f5542a9974e8 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 8 May 2025 14:47:23 +0200 Subject: [PATCH 165/271] OSSRH to Maven Central publishing migration (#220) --- .github/workflows/ci.yml | 2 +- pom.xml | 38 +++++++++++++++----------------------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b303a873..ba54aec9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: with: java-version: '11' distribution: 'temurin' - server-id: ossrh + server-id: central server-username: OSSRH_USER server-password: OSSRH_PASS gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} diff --git a/pom.xml b/pom.xml index c3e3be72..352c03a2 100644 --- a/pom.xml +++ b/pom.xml @@ -103,9 +103,10 @@ 3.2.7 - org.sonatype.plugins - nexus-staging-maven-plugin - 1.7.0 + org.sonatype.central + central-publishing-maven-plugin + 0.7.0 + true org.jacoco @@ -175,18 +176,6 @@ - - - org.sonatype.plugins - nexus-staging-maven-plugin - true - - ossrh - https://oss.sonatype.org/ - true - - - org.jacoco @@ -226,6 +215,17 @@ + + + + org.sonatype.central + central-publishing-maven-plugin + true + + central + true + + @@ -300,12 +300,4 @@ - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - From 5f8934628270b54e5446555729e9d513005228da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Aug 2025 11:12:21 +0200 Subject: [PATCH 166/271] Bump actions/checkout from 4 to 5 (#227) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba54aec9..9eefb941 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Maven Central Repository uses: actions/setup-java@v4 with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index cddbc77e..253fce09 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -7,7 +7,7 @@ jobs: build_and_test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up JDK 11 uses: actions/setup-java@v4 with: From 6270a045c9e52178524e9af0d900daa261cc4274 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Aug 2025 11:12:34 +0200 Subject: [PATCH 167/271] Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.1 to 3.11.2 (#221) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.1 to 3.11.2. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.1...maven-javadoc-plugin-3.11.2) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.11.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 352c03a2..8b894f05 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.1 + 3.11.2 org.apache.maven.plugins From a39e271c6ca57cdea656eff038d7bafb44040662 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Aug 2025 11:12:44 +0200 Subject: [PATCH 168/271] Bump org.apache.maven.plugins:maven-compiler-plugin (#222) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.13.0 to 3.14.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.13.0...maven-compiler-plugin-3.14.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8b894f05..47303b0c 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.14.0 org.apache.maven.plugins From 1e7c485bb7863fdfc461ec4143e47f52a0f1a242 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Aug 2025 11:12:55 +0200 Subject: [PATCH 169/271] Bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.2 to 3.5.3 (#224) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.5.2 to 3.5.3. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.2...surefire-3.5.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 47303b0c..41c35bb7 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.2 + 3.5.3 org.apache.maven.plugins From 3b61b0804157b26d21788c4df08800995ef606bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 Aug 2025 11:13:08 +0200 Subject: [PATCH 170/271] Bump org.testng:testng from 7.10.2 to 7.11.0 (#225) Bumps [org.testng:testng](https://github.com/testng-team/testng) from 7.10.2 to 7.11.0. - [Release notes](https://github.com/testng-team/testng/releases) - [Changelog](https://github.com/testng-team/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/testng-team/testng/compare/7.10.2...7.11.0) --- updated-dependencies: - dependency-name: org.testng:testng dependency-version: 7.11.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 41c35bb7..33a65bd7 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ org.testng testng - 7.10.2 + 7.11.0 test From f05068b95c29cb72348ba2d783263d33545e85f9 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 17 Aug 2025 14:27:53 +0200 Subject: [PATCH 171/271] 228 Add get boundaries to Spatial Index (#229) * Add getBoundaries to SpatialIndex * Fix issue with empty boundaries --- src/main/java/org/gephi/graph/api/Rect2D.java | 22 ++ .../org/gephi/graph/api/SpatialIndex.java | 8 + .../gephi/graph/impl/GraphViewDecorator.java | 40 +++ .../org/gephi/graph/impl/NodesQuadTree.java | 40 ++- .../gephi/graph/impl/SpatialIndexImpl.java | 5 + .../graph/impl/GraphViewDecoratorTest.java | 191 +++++++++++ .../gephi/graph/impl/NodesQuadTreeTest.java | 309 +++++++++++++++++- 7 files changed, 606 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/Rect2D.java b/src/main/java/org/gephi/graph/api/Rect2D.java index bc2b34fb..98ab3876 100644 --- a/src/main/java/org/gephi/graph/api/Rect2D.java +++ b/src/main/java/org/gephi/graph/api/Rect2D.java @@ -173,4 +173,26 @@ public boolean contains(float minX, float minY, float maxX, float maxY) { public boolean intersects(float minX, float minY, float maxX, float maxY) { return this.minX <= maxX && minX <= this.maxX && this.maxY >= minY && maxY >= this.minY; } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + Rect2D rect2D = (Rect2D) obj; + return Float.compare(rect2D.minX, minX) == 0 && Float.compare(rect2D.minY, minY) == 0 && Float + .compare(rect2D.maxX, maxX) == 0 && Float.compare(rect2D.maxY, maxY) == 0; + } + + @Override + public int hashCode() { + int result = (minX != +0.0f ? Float.floatToIntBits(minX) : 0); + result = 31 * result + (minY != +0.0f ? Float.floatToIntBits(minY) : 0); + result = 31 * result + (maxX != +0.0f ? Float.floatToIntBits(maxX) : 0); + result = 31 * result + (maxY != +0.0f ? Float.floatToIntBits(maxY) : 0); + return result; + } } diff --git a/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java index 4efb291e..997c9040 100644 --- a/src/main/java/org/gephi/graph/api/SpatialIndex.java +++ b/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -37,4 +37,12 @@ public interface SpatialIndex { * @return edges in the area */ EdgeIterable getEdgesInArea(Rect2D rect); + + /** + * Returns the bounding rectangle that contains all nodes in the graph. The + * boundaries are calculated based on each node's position and size. + * + * @return the bounding rectangle, or null if there are no nodes + */ + Rect2D getBoundaries(); } diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index db87b820..79971b30 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -845,6 +845,46 @@ public EdgeIterable getEdgesInArea(Rect2D rect) { return new EdgeIterableWrapper(new EdgeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); } + @Override + public Rect2D getBoundaries() { + graphStore.autoReadLock(); + try { + float minX = Float.POSITIVE_INFINITY; + float minY = Float.POSITIVE_INFINITY; + float maxX = Float.NEGATIVE_INFINITY; + float maxY = Float.NEGATIVE_INFINITY; + + boolean hasNodes = false; + + // Iterate only through nodes visible in this view + for (Node node : getNodes()) { + hasNodes = true; + final float x = node.x(); + final float y = node.y(); + final float size = node.size(); + + final float nodeMinX = x - size; + final float nodeMinY = y - size; + final float nodeMaxX = x + size; + final float nodeMaxY = y + size; + + if (nodeMinX < minX) + minX = nodeMinX; + if (nodeMinY < minY) + minY = nodeMinY; + if (nodeMaxX > maxX) + maxX = nodeMaxX; + if (nodeMaxY > maxY) + maxY = nodeMaxY; + } + + return hasNodes ? new Rect2D(minX, minY, maxX, maxY) : new Rect2D(Float.NEGATIVE_INFINITY, + Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); + } finally { + graphStore.autoReadUnlock(); + } + } + protected final class NodeViewIterator implements Iterator { private final Iterator nodeIterator; diff --git a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index c32de592..172c3490 100644 --- a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -16,8 +16,6 @@ /** * Adapted from https://bitbucket.org/C3/quadtree/wiki/Home * - * TODO: unit tests!! - * * @author Eduardo Ramos */ public class NodesQuadTree { @@ -171,6 +169,44 @@ public int getDepth() { return depth; } + public Rect2D getBoundaries() { + readLock(); + try { + NodeIterable allNodes = getAllNodes(); + + float minX = Float.POSITIVE_INFINITY; + float minY = Float.POSITIVE_INFINITY; + float maxX = Float.NEGATIVE_INFINITY; + float maxY = Float.NEGATIVE_INFINITY; + + boolean hasNodes = false; + + for (Node node : allNodes) { + SpatialNodeDataImpl spatialData = ((NodeImpl) node).getSpatialData(); + if (spatialData != null) { + hasNodes = true; + if (spatialData.minX < minX) { + minX = spatialData.minX; + } + if (spatialData.minY < minY) { + minY = spatialData.minY; + } + if (spatialData.maxX > maxX) { + maxX = spatialData.maxX; + } + if (spatialData.maxY > maxY) { + maxY = spatialData.maxY; + } + } + } + + return hasNodes ? new Rect2D(minX, minY, maxX, maxY) : new Rect2D(Float.NEGATIVE_INFINITY, + Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); + } finally { + readUnlock(); + } + } + protected class QuadTreeNode { private Set objects = null; diff --git a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java index b3d63f6c..75389855 100644 --- a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java @@ -59,6 +59,11 @@ protected void moveNode(final NodeImpl node) { nodesTree.updateNode(node, minX, minY, maxX, maxY); } + @Override + public Rect2D getBoundaries() { + return nodesTree.getBoundaries(); + } + protected class EdgeIterator implements Iterator { private final Iterator nodeItr; diff --git a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java index 09c7674a..ff60e577 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java @@ -20,12 +20,15 @@ import java.util.Arrays; import java.util.Collections; import java.util.Random; +import org.gephi.graph.api.Configuration; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Element; import org.gephi.graph.api.ElementIterable; import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; +import org.gephi.graph.api.Rect2D; +import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.UndirectedSubgraph; import org.testng.Assert; import org.testng.annotations.Test; @@ -918,6 +921,189 @@ private GraphStore convertToStore(GraphViewImpl view) { return store; } + @Test + public void testGetBoundariesEmptyView() { + GraphStore graphStore = GraphGenerator.generateEmptyGraphStore(getSpatialConfig()); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + DirectedSubgraph graph = store.getDirectedGraph(view); + + Assert.assertEquals(new Rect2D(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, + Float.POSITIVE_INFINITY), graph.getSpatialIndex().getBoundaries()); + } + + @Test + public void testGetBoundariesSingleNodeInView() { + GraphStore graphStore = GraphGenerator.generateEmptyGraphStore(getSpatialConfig()); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + // Add a node to the graph store + NodeImpl node1 = (NodeImpl) graphStore.factory.newNode("1"); + node1.setPosition(100, 200); + node1.setSize(10); + graphStore.addNode(node1); + + NodeImpl node2 = (NodeImpl) graphStore.factory.newNode("2"); + node2.setPosition(500, 600); + node2.setSize(20); + graphStore.addNode(node2); + + // Add only node1 to the view + view.addNode(node1); + + DirectedSubgraph graph = store.getDirectedGraph(view); + + // Should return boundaries only for node1 + Rect2D boundaries = graph.getSpatialIndex().getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, 90f); // 100 - 10 + Assert.assertEquals(boundaries.minY, 190f); // 200 - 10 + Assert.assertEquals(boundaries.maxX, 110f); // 100 + 10 + Assert.assertEquals(boundaries.maxY, 210f); // 200 + 10 + } + + @Test + public void testGetBoundariesMultipleNodesInView() { + GraphStore graphStore = GraphGenerator.generateEmptyGraphStore(getSpatialConfig()); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + // Add nodes to the graph store + NodeImpl node1 = (NodeImpl) graphStore.factory.newNode("1"); + node1.setPosition(0, 0); + node1.setSize(5); + graphStore.addNode(node1); + + NodeImpl node2 = (NodeImpl) graphStore.factory.newNode("2"); + node2.setPosition(100, 200); + node2.setSize(10); + graphStore.addNode(node2); + + NodeImpl node3 = (NodeImpl) graphStore.factory.newNode("3"); + node3.setPosition(500, 600); // This node won't be in the view + node3.setSize(20); + graphStore.addNode(node3); + + // Add only node1 and node2 to the view + view.addNode(node1); + view.addNode(node2); + + DirectedSubgraph graph = store.getDirectedGraph(view); + + // Should return boundaries only for node1 and node2 + Rect2D boundaries = graph.getSpatialIndex().getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, -5f); // node1: 0 - 5 + Assert.assertEquals(boundaries.minY, -5f); // node1: 0 - 5 + Assert.assertEquals(boundaries.maxX, 110f); // node2: 100 + 10 + Assert.assertEquals(boundaries.maxY, 210f); // node2: 200 + 10 + } + + @Test + public void testGetBoundariesViewSubsetVsFullGraph() { + GraphStore graphStore = GraphGenerator.generateEmptyGraphStore(getSpatialConfig()); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + // Add nodes to the graph store + NodeImpl node1 = (NodeImpl) graphStore.factory.newNode("1"); + node1.setPosition(0, 0); + node1.setSize(5); + graphStore.addNode(node1); + + NodeImpl node2 = (NodeImpl) graphStore.factory.newNode("2"); + node2.setPosition(100, 200); + node2.setSize(10); + graphStore.addNode(node2); + + NodeImpl node3 = (NodeImpl) graphStore.factory.newNode("3"); + node3.setPosition(-50, -100); + node3.setSize(15); + graphStore.addNode(node3); + + // Add only first two nodes to the view + view.addNode(node1); + view.addNode(node2); + + DirectedSubgraph viewGraph = store.getDirectedGraph(view); + DirectedSubgraph fullGraph = graphStore; + + // Get boundaries for both + Rect2D viewBoundaries = viewGraph.getSpatialIndex().getBoundaries(); + Rect2D fullBoundaries = graphStore.spatialIndex.getBoundaries(); + + // View boundaries should only include node1 and node2 + Assert.assertNotNull(viewBoundaries); + Assert.assertEquals(viewBoundaries.minX, -5f); // node1: 0 - 5 + Assert.assertEquals(viewBoundaries.minY, -5f); // node1: 0 - 5 + Assert.assertEquals(viewBoundaries.maxX, 110f); // node2: 100 + 10 + Assert.assertEquals(viewBoundaries.maxY, 210f); // node2: 200 + 10 + + // Full graph boundaries should include all nodes + Assert.assertNotNull(fullBoundaries); + Assert.assertEquals(fullBoundaries.minX, -65f); // node3: -50 - 15 + Assert.assertEquals(fullBoundaries.minY, -115f); // node3: -100 - 15 + Assert.assertEquals(fullBoundaries.maxX, 110f); // node2: 100 + 10 + Assert.assertEquals(fullBoundaries.maxY, 210f); // node2: 200 + 10 + + // They should be different + Assert.assertFalse(viewBoundaries.minX == fullBoundaries.minX); + Assert.assertFalse(viewBoundaries.minY == fullBoundaries.minY); + } + + @Test + public void testGetBoundariesAfterViewChanges() { + GraphStore graphStore = GraphGenerator.generateEmptyGraphStore(getSpatialConfig()); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + // Add nodes to the graph store + NodeImpl node1 = (NodeImpl) graphStore.factory.newNode("1"); + node1.setPosition(0, 0); + node1.setSize(5); + graphStore.addNode(node1); + + NodeImpl node2 = (NodeImpl) graphStore.factory.newNode("2"); + node2.setPosition(100, 200); + node2.setSize(10); + graphStore.addNode(node2); + + DirectedSubgraph graph = store.getDirectedGraph(view); + + // Initially empty view + Rect2D boundaries = graph.getSpatialIndex().getBoundaries(); + Rect2D expected = new Rect2D(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, + Float.POSITIVE_INFINITY); + Assert.assertEquals(expected, boundaries); + + // Add first node to view + view.addNode(node1); + Rect2D boundaries1 = graph.getSpatialIndex().getBoundaries(); + Assert.assertNotNull(boundaries1); + Assert.assertEquals(boundaries1.minX, -5f); + Assert.assertEquals(boundaries1.maxX, 5f); + + // Add second node to view + view.addNode(node2); + Rect2D boundaries2 = graph.getSpatialIndex().getBoundaries(); + Assert.assertNotNull(boundaries2); + Assert.assertEquals(boundaries2.minX, -5f); + Assert.assertEquals(boundaries2.maxX, 110f); + + // Remove first node from view + view.removeNode(node1); + Rect2D boundaries3 = graph.getSpatialIndex().getBoundaries(); + Assert.assertNotNull(boundaries3); + Assert.assertEquals(boundaries3.minX, 90f); // Only node2 remains + Assert.assertEquals(boundaries3.maxX, 110f); + + // Remove last node from view + view.removeNode(node2); + Assert.assertEquals(expected, graph.getSpatialIndex().getBoundaries()); + } + private void addSomeElements(GraphStore store, GraphViewImpl view) { double perc = 0.8; Random rand = new Random(98324); @@ -934,4 +1120,9 @@ private void addSomeElements(GraphStore store, GraphViewImpl view) { } } } + + // Configuration with spatial index enabled + private Configuration getSpatialConfig() { + return Configuration.builder().enableSpatialIndex(true).build(); + } } diff --git a/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java b/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java index d294d6a6..4ea74081 100644 --- a/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java +++ b/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java @@ -1,6 +1,5 @@ package org.gephi.graph.impl; -import java.util.Arrays; import java.util.Collection; import java.util.Random; import org.gephi.graph.api.Node; @@ -222,13 +221,309 @@ private void assertEmpty(NodeIterable iterable) { Assert.assertEquals(iterable.toCollection().size(), 0); } - private String listIds(Collection nodes) { - StringBuilder sb = new StringBuilder(); + @Test + public void testGetBoundariesEmpty() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, Float.NEGATIVE_INFINITY); + Assert.assertEquals(boundaries.minY, Float.NEGATIVE_INFINITY); + Assert.assertEquals(boundaries.maxX, Float.POSITIVE_INFINITY); + Assert.assertEquals(boundaries.maxY, Float.POSITIVE_INFINITY); + } - for (Node node : nodes) { - sb.append(node.getId()).append(' '); - } + @Test + public void testGetBoundariesSingleNode() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + NodeImpl node = new NodeImpl("0"); + node.setPosition(100, 200); + node.setSize(10); + + q.addNode(node); + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, 90f); // x - size + Assert.assertEquals(boundaries.minY, 190f); // y - size + Assert.assertEquals(boundaries.maxX, 110f); // x + size + Assert.assertEquals(boundaries.maxY, 210f); // y + size + } + + @Test + public void testGetBoundariesMultipleNodes() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl n1 = new NodeImpl("1"); + n1.setPosition(0, 0); + n1.setSize(5); + + NodeImpl n2 = new NodeImpl("2"); + n2.setPosition(100, 200); + n2.setSize(10); + + NodeImpl n3 = new NodeImpl("3"); + n3.setPosition(-50, -100); + n3.setSize(15); + + q.addNode(n1); + q.addNode(n2); + q.addNode(n3); + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, -65f); // n3: -50 - 15 + Assert.assertEquals(boundaries.minY, -115f); // n3: -100 - 15 + Assert.assertEquals(boundaries.maxX, 110f); // n2: 100 + 10 + Assert.assertEquals(boundaries.maxY, 210f); // n2: 200 + 10 + } + + @Test + public void testGetBoundariesAfterClear() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl node = new NodeImpl("0"); + node.setPosition(100, 200); + node.setSize(10); + q.addNode(node); + + // Should have boundaries initially + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertNotEquals(boundaries.minX, Float.NEGATIVE_INFINITY); + + // After clear, should return empty rectangle + q.clear(); + boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, Float.NEGATIVE_INFINITY); + Assert.assertEquals(boundaries.minY, Float.NEGATIVE_INFINITY); + Assert.assertEquals(boundaries.maxX, Float.POSITIVE_INFINITY); + Assert.assertEquals(boundaries.maxY, Float.POSITIVE_INFINITY); + } + + @Test + public void testGetBoundariesAfterRemove() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl n1 = new NodeImpl("1"); + n1.setPosition(0, 0); + n1.setSize(5); + + NodeImpl n2 = new NodeImpl("2"); + n2.setPosition(100, 200); + n2.setSize(10); + + q.addNode(n1); + q.addNode(n2); + + // Remove one node + q.removeNode(n1); + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, 90f); // Only n2 remains + Assert.assertEquals(boundaries.minY, 190f); + Assert.assertEquals(boundaries.maxX, 110f); + Assert.assertEquals(boundaries.maxY, 210f); + + // Remove last node + q.removeNode(n2); + boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, Float.NEGATIVE_INFINITY); + Assert.assertEquals(boundaries.minY, Float.NEGATIVE_INFINITY); + Assert.assertEquals(boundaries.maxX, Float.POSITIVE_INFINITY); + Assert.assertEquals(boundaries.maxY, Float.POSITIVE_INFINITY); + } + + @Test + public void testGetBoundariesAfterRemoveBoundaryNode() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl n1 = new NodeImpl("1"); + n1.setPosition(0, 0); + n1.setSize(5); + + NodeImpl n2 = new NodeImpl("2"); + n2.setPosition(100, 200); + n2.setSize(10); // This will be at the boundary + + NodeImpl n3 = new NodeImpl("3"); + n3.setPosition(50, 100); + n3.setSize(8); + + q.addNode(n1); + q.addNode(n2); + q.addNode(n3); + + // Remove the node that was at the max boundary + q.removeNode(n2); + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, -5f); // n1: 0 - 5 + Assert.assertEquals(boundaries.minY, -5f); // n1: 0 - 5 + Assert.assertEquals(boundaries.maxX, 58f); // n3: 50 + 8 + Assert.assertEquals(boundaries.maxY, 108f); // n3: 100 + 8 + } + + @Test + public void testGetBoundariesAfterUpdate() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl node = new NodeImpl("0"); + node.setPosition(0, 0); + node.setSize(5); + q.addNode(node); + + // Update position + node.setPosition(100, 200); + node.setSize(15); + q.updateNode(node, 85, 185, 115, 215); // 100±15, 200±15 + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, 85f); + Assert.assertEquals(boundaries.minY, 185f); + Assert.assertEquals(boundaries.maxX, 115f); + Assert.assertEquals(boundaries.maxY, 215f); + } + + @Test + public void testGetBoundariesAfterUpdateBoundaryNode() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl n1 = new NodeImpl("1"); + n1.setPosition(0, 0); + n1.setSize(5); + + NodeImpl n2 = new NodeImpl("2"); + n2.setPosition(100, 200); + n2.setSize(10); + + q.addNode(n1); + q.addNode(n2); + + // Move the boundary node to a smaller position + n2.setPosition(50, 100); + n2.setSize(5); + q.updateNode(n2, 45, 95, 55, 105); + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, -5f); // n1: 0 - 5 + Assert.assertEquals(boundaries.minY, -5f); // n1: 0 - 5 + Assert.assertEquals(boundaries.maxX, 55f); // n2: 50 + 5 + Assert.assertEquals(boundaries.maxY, 105f); // n2: 100 + 5 + } + + @Test + public void testGetBoundariesWithZeroSizeNodes() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl n1 = new NodeImpl("1"); + n1.setPosition(10, 20); + n1.setSize(0); + + NodeImpl n2 = new NodeImpl("2"); + n2.setPosition(-5, -10); + n2.setSize(0); + + q.addNode(n1); + q.addNode(n2); + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, -5f); + Assert.assertEquals(boundaries.minY, -10f); + Assert.assertEquals(boundaries.maxX, 10f); + Assert.assertEquals(boundaries.maxY, 20f); + } + + @Test + public void testGetBoundariesWithOutOfBoundsNodes() { + NodesQuadTree q = new NodesQuadTree(new Rect2D(-10, -10, 10, 10)); + + NodeImpl n1 = new NodeImpl("1"); + n1.setPosition(100, 200); // Way out of bounds + n1.setSize(5); + + NodeImpl n2 = new NodeImpl("2"); + n2.setPosition(0, 0); // Within bounds + n2.setSize(2); + + q.addNode(n1); + q.addNode(n2); + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, -2f); // n2: 0 - 2 + Assert.assertEquals(boundaries.minY, -2f); // n2: 0 - 2 + Assert.assertEquals(boundaries.maxX, 105f); // n1: 100 + 5 + Assert.assertEquals(boundaries.maxY, 205f); // n1: 200 + 5 + } + + @Test + public void testGetBoundariesWithNegativeCoordinates() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl n1 = new NodeImpl("1"); + n1.setPosition(-100, -200); + n1.setSize(10); + + NodeImpl n2 = new NodeImpl("2"); + n2.setPosition(-50, -150); + n2.setSize(5); + + q.addNode(n1); + q.addNode(n2); + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, -110f); // n1: -100 - 10 + Assert.assertEquals(boundaries.minY, -210f); // n1: -200 - 10 + Assert.assertEquals(boundaries.maxX, -45f); // n2: -50 + 5 + Assert.assertEquals(boundaries.maxY, -145f); // n2: -150 + 5 + } + + @Test + public void testGetBoundariesWithMixedCoordinates() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl n1 = new NodeImpl("1"); + n1.setPosition(-50, 100); + n1.setSize(20); + + NodeImpl n2 = new NodeImpl("2"); + n2.setPosition(75, -80); + n2.setSize(15); + + q.addNode(n1); + q.addNode(n2); + + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, -70f); // n1: -50 - 20 + Assert.assertEquals(boundaries.minY, -95f); // n2: -80 - 15 + Assert.assertEquals(boundaries.maxX, 90f); // n2: 75 + 15 + Assert.assertEquals(boundaries.maxY, 120f); // n1: 100 + 20 + } + + @Test + public void testGetBoundariesSingleNodeAtOrigin() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + NodeImpl node = new NodeImpl("0"); + node.setPosition(0, 0); + node.setSize(1); + + q.addNode(node); - return sb.toString(); + Rect2D boundaries = q.getBoundaries(); + Assert.assertNotNull(boundaries); + Assert.assertEquals(boundaries.minX, -1f); + Assert.assertEquals(boundaries.minY, -1f); + Assert.assertEquals(boundaries.maxX, 1f); + Assert.assertEquals(boundaries.maxY, 1f); } } From 12d3dd066cbb879f5948db6dc692166d466e7369 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 17 Aug 2025 15:41:26 +0200 Subject: [PATCH 172/271] Add two additional tests around edge weight --- .../org/gephi/graph/impl/IndexStoreTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java index 17d8af87..ce18d9b0 100644 --- a/src/test/java/org/gephi/graph/impl/IndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/IndexStoreTest.java @@ -301,6 +301,34 @@ public void testEdgePropertyIndices() { Assert.assertEquals(mainIndex.count(weigthCol, 1.0), 1); } + @Test + public void testEdgeWeigthSimple() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + ColumnStore columnStore = graphStore.edgeTable.store; + IndexImpl mainIndex = columnStore.indexStore.mainIndex; + + Column weigthCol = columnStore.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + + Assert.assertEquals(mainIndex.getMinValue(weigthCol), 1.0); + Assert.assertEquals(mainIndex.getMaxValue(weigthCol), 1.0); + } + + @Test + public void testEdgeWeigthInView() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + ColumnStore columnStore = graphStore.edgeTable.store; + + GraphView view = graphStore.viewStore.createView(); + Subgraph graph = graphStore.viewStore.getGraph(view); + graph.fill(); + IndexImpl index = columnStore.indexStore.createViewIndex(graph); + + Column weigthCol = columnStore.getColumnByIndex(GraphStoreConfiguration.EDGE_WEIGHT_INDEX); + + Assert.assertEquals(index.getMinValue(weigthCol), 1.0); + Assert.assertEquals(index.getMaxValue(weigthCol), 1.0); + } + @Test public void testCreateViewIndex() { GraphStore graphStore = generateBasicGraphStoreWithColumns(); From 0776c75ae4f59467569f4f8364ebd243e3f0c106 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 17 Aug 2025 15:44:53 +0200 Subject: [PATCH 173/271] Set version to 0.7.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 33a65bd7..ee68fa31 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.7.3-SNAPSHOT + 0.7.3 jar GraphStore From 82a3230b33f16e3c892b10b739071a50b6754fe7 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 17 Aug 2025 15:52:11 +0200 Subject: [PATCH 174/271] Set version to 0.7.4-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ee68fa31..9eec80b8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.7.3 + 0.7.4-SNAPSHOT jar GraphStore From f4ad57d0f7ac7b6cc08bd7bbdd39d6418c74b398 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 18 Aug 2025 19:16:56 +0200 Subject: [PATCH 175/271] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 50ba7f56..0a5e0aab 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.7.2 + 0.7.3 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.7.2' +compile 'org.gephi:graphstore:0.7.3' ``` ## Dependencies From b3b085bc7fd8fe327bbdadda4ef2e85e0155a9d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 09:18:01 +0200 Subject: [PATCH 176/271] Bump actions/setup-java from 4 to 5 (#234) Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5. - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-java dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eefb941..f16047b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up Maven Central Repository - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '11' distribution: 'temurin' diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 253fce09..8ecec0e6 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Set up JDK 11 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '11' distribution: 'temurin' From c3563403f865b4765db93dc748000cd7385ae914 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 09:18:13 +0200 Subject: [PATCH 177/271] Bump org.codehaus.mojo:build-helper-maven-plugin from 3.6.0 to 3.6.1 (#233) Bumps [org.codehaus.mojo:build-helper-maven-plugin](https://github.com/mojohaus/build-helper-maven-plugin) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/mojohaus/build-helper-maven-plugin/releases) - [Commits](https://github.com/mojohaus/build-helper-maven-plugin/compare/3.6.0...3.6.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:build-helper-maven-plugin dependency-version: 3.6.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9eec80b8..9876cfaf 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ org.codehaus.mojo build-helper-maven-plugin - 3.6.0 + 3.6.1 From 3c4eb76882d8055dd010846b30cdc282129ce5ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 09:18:31 +0200 Subject: [PATCH 178/271] Bump org.apache.maven.plugins:maven-gpg-plugin from 3.2.7 to 3.2.8 (#232) Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.7 to 3.2.8. - [Release notes](https://github.com/apache/maven-gpg-plugin/releases) - [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.7...maven-gpg-plugin-3.2.8) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-gpg-plugin dependency-version: 3.2.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9876cfaf..cd6eb4f0 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.2.7 + 3.2.8 org.sonatype.central From 0cebc68b5b2fa84b91dfddcd78f48d3da7574f8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 09:18:40 +0200 Subject: [PATCH 179/271] Bump org.jacoco:jacoco-maven-plugin from 0.8.12 to 0.8.13 (#231) Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.12 to 0.8.13. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.12...v0.8.13) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-version: 0.8.13 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd6eb4f0..acb914f2 100644 --- a/pom.xml +++ b/pom.xml @@ -111,7 +111,7 @@ org.jacoco jacoco-maven-plugin - 0.8.12 + 0.8.13 org.eluder.coveralls From 1b31e2fcd21367b8ce25bf5392d4e9d30e49f56c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 09:18:52 +0200 Subject: [PATCH 180/271] Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.2 to 3.11.3 (#230) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.2 to 3.11.3. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.2...maven-javadoc-plugin-3.11.3) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.11.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index acb914f2..ec7cb929 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.2 + 3.11.3 org.apache.maven.plugins From 42d2183def7b4ea047788bf3e2ec5df5ab602148 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 22 Sep 2025 20:30:29 +0200 Subject: [PATCH 181/271] Implement spliterator for both nodes and edges (#240) * Implement spliterator for both nodes and edges, while keeping fallback on iterator() * Locking on toArray, toCollection and toSet for wrappers and parallel * Optimise imports * Set version to 0.8.0-SNAPSHOT * Fix sizing issue with node's spliterator and create custom one for graph view * Also fix edge spliterator, introducing both SIZED and non SIZED options * Set default node block to 8192 * Improve unit tests for ede spliterator --- pom.xml | 2 +- .../org/gephi/graph/api/EdgeIterable.java | 23 +- .../org/gephi/graph/api/ElementIterable.java | 28 ++ .../org/gephi/graph/api/NodeIterable.java | 21 ++ .../gephi/graph/impl/ColumnObserverImpl.java | 8 +- .../gephi/graph/impl/EdgeIterableWrapper.java | 21 +- .../java/org/gephi/graph/impl/EdgeStore.java | 247 ++++++++++++++++ .../graph/impl/ElementIterableWrapper.java | 69 +++-- .../gephi/graph/impl/GraphObserverImpl.java | 14 +- .../java/org/gephi/graph/impl/GraphStore.java | 84 +++--- .../graph/impl/GraphStoreConfiguration.java | 6 +- .../org/gephi/graph/impl/GraphVersion.java | 8 + .../gephi/graph/impl/GraphViewDecorator.java | 274 +++++++++++++++--- .../gephi/graph/impl/NodeIterableWrapper.java | 21 +- .../java/org/gephi/graph/impl/NodeStore.java | 160 +++++++++- .../gephi/graph/impl/SpatialIndexImpl.java | 3 +- .../gephi/graph/impl/UndirectedDecorator.java | 37 ++- .../org/gephi/graph/impl/BasicGraphStore.java | 19 +- .../org/gephi/graph/impl/EdgeStoreTest.java | 148 +++++++++- .../gephi/graph/impl/EmptyIterableTest.java | 29 ++ .../org/gephi/graph/impl/GraphStoreTest.java | 71 +++-- .../graph/impl/GraphViewDecoratorTest.java | 113 ++++---- .../gephi/graph/impl/GraphViewStoreTest.java | 2 +- .../org/gephi/graph/impl/NodeStoreTest.java | 164 +++++++++++ .../graph/impl/UndirectedDecoratorTest.java | 10 +- 25 files changed, 1350 insertions(+), 232 deletions(-) diff --git a/pom.xml b/pom.xml index ec7cb929..350d84df 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.7.4-SNAPSHOT + 0.8.0-SNAPSHOT jar GraphStore diff --git a/src/main/java/org/gephi/graph/api/EdgeIterable.java b/src/main/java/org/gephi/graph/api/EdgeIterable.java index 399c848e..cc9b44dd 100644 --- a/src/main/java/org/gephi/graph/api/EdgeIterable.java +++ b/src/main/java/org/gephi/graph/api/EdgeIterable.java @@ -20,6 +20,8 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; /** * An edge iterable. @@ -63,10 +65,24 @@ public interface EdgeIterable extends ElementIterable { @Override public Set toSet(); + /** + * Returns a Spliterator over the edges. + *

+ * Implementations return a splittable, sized, fail-fast spliterator suitable + * for parallel streams. When not possible, a non-splittable spliterator is + * returned. + * + * @return edge spliterator + */ + @Override + default Spliterator spliterator() { + return ElementIterable.super.spliterator(); + } + /** * Empty edge iterable. */ - static final class EdgeIterableEmpty implements Iterator, EdgeIterable { + final class EdgeIterableEmpty implements Iterator, EdgeIterable { @Override public boolean hasNext() { @@ -88,6 +104,11 @@ public Iterator iterator() { return this; } + @Override + public Spliterator spliterator() { + return Spliterators.emptySpliterator(); + } + @Override public Edge[] toArray() { return new Edge[0]; diff --git a/src/main/java/org/gephi/graph/api/ElementIterable.java b/src/main/java/org/gephi/graph/api/ElementIterable.java index a3ec3e27..2170f492 100644 --- a/src/main/java/org/gephi/graph/api/ElementIterable.java +++ b/src/main/java/org/gephi/graph/api/ElementIterable.java @@ -20,6 +20,10 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * Element iterable. @@ -41,6 +45,25 @@ public interface ElementIterable extends Iterable { @Override public Iterator iterator(); + /** + * Creates a new sequential stream, based on the spliterator returned. + * + * @return stream + */ + default Stream stream() { + return StreamSupport.stream(spliterator(), false); + } + + /** + * Creates a new sequential and parallel stream, based on the spliterator + * returned. + * + * @return stream + */ + default Stream parallelStream() { + return StreamSupport.stream(spliterator(), true); + } + /** * Returns the iterator content as an array. * @@ -92,6 +115,11 @@ public Iterator iterator() { return this; } + @Override + public Spliterator spliterator() { + return Spliterators.emptySpliterator(); + } + @Override public Element[] toArray() { return new Node[0]; diff --git a/src/main/java/org/gephi/graph/api/NodeIterable.java b/src/main/java/org/gephi/graph/api/NodeIterable.java index 88601682..30d3b2db 100644 --- a/src/main/java/org/gephi/graph/api/NodeIterable.java +++ b/src/main/java/org/gephi/graph/api/NodeIterable.java @@ -20,6 +20,8 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; /** * A node iterable. @@ -63,6 +65,20 @@ public interface NodeIterable extends ElementIterable { @Override public Set toSet(); + /** + * Returns a Spliterator over the nodes. + *

+ * Implementations return a splittable, sized, fail-fast spliterator suitable + * for parallel streams. When not possible, a non-splittable spliterator is + * returned. + * + * @return node spliterator + */ + @Override + default Spliterator spliterator() { + return ElementIterable.super.spliterator(); + } + /** * Empty node iterable. */ @@ -88,6 +104,11 @@ public Iterator iterator() { return this; } + @Override + public Spliterator spliterator() { + return Spliterators.emptySpliterator(); + } + @Override public Node[] toArray() { return new Node[0]; diff --git a/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java b/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java index c8761bc3..d90aea76 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java @@ -19,6 +19,7 @@ import cern.colt.bitvector.QuickBitVector; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectList; +import it.unimi.dsi.fastutil.objects.ObjectLists; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnDiff; @@ -155,7 +156,8 @@ protected final class NodeColumnDiffImpl extends ColumnDiffImpl { @Override public NodeIterable getTouchedElements() { if (!touchedElements.isEmpty()) { - return graphStore.getNodeIterableWrapper(touchedElements.iterator(), false); + return new NodeIterableWrapper(() -> ObjectLists.unmodifiable(touchedElements).iterator(), + () -> ObjectLists.unmodifiable(touchedElements).spliterator(), null); } return NodeIterable.NodeIterableEmpty.EMPTY; @@ -167,8 +169,8 @@ protected final class EdgeColumnDiffImpl extends ColumnDiffImpl { @Override public EdgeIterable getTouchedElements() { if (!touchedElements.isEmpty()) { - return graphStore.getEdgeIterableWrapper(touchedElements.iterator(), false); - + return new EdgeIterableWrapper(() -> ObjectLists.unmodifiable(touchedElements).iterator(), + () -> ObjectLists.unmodifiable(touchedElements).spliterator(), null); } return EdgeIterable.EdgeIterableEmpty.EMPTY; } diff --git a/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java b/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java index 8db89823..d51e7b38 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/EdgeIterableWrapper.java @@ -16,21 +16,32 @@ package org.gephi.graph.impl; import java.util.Iterator; +import java.util.Spliterator; +import java.util.function.Supplier; +import java.util.stream.StreamSupport; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; public class EdgeIterableWrapper extends ElementIterableWrapper implements EdgeIterable { - public EdgeIterableWrapper(Iterator iterator) { - super(iterator); + public EdgeIterableWrapper(Supplier> iteratorSupplier, GraphLockImpl lock) { + super(iteratorSupplier, lock); } - public EdgeIterableWrapper(Iterator iterator, GraphLockImpl lock) { - super(iterator, lock); + public EdgeIterableWrapper(Supplier> iteratorSupplier, Supplier> spliteratorSupplier, GraphLockImpl lock) { + super(iteratorSupplier, spliteratorSupplier, lock); } @Override public Edge[] toArray() { - return toArray(new Edge[0]); + if (parallelPossible && lock != null) { + lock.readLock(); + try { + return StreamSupport.stream(spliterator(), true).toArray(Edge[]::new); + } finally { + lock.readUnlock(); + } + } + return StreamSupport.stream(spliterator(), parallelPossible).toArray(Edge[]::new); } } diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 7328bb01..497c7a00 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -25,10 +25,16 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.ConcurrentModificationException; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Node; @@ -348,6 +354,49 @@ public EdgeStoreIterator iterator() { return new EdgeStoreIterator(); } + @Override + public Spliterator spliterator() { + int end = blocksCount; + return new EdgeSpliterator(0, end); + } + + @Override + public Stream stream() { + return StreamSupport.stream(spliterator(), false); + } + + @Override + public Stream parallelStream() { + return StreamSupport.stream(spliterator(), true); + } + + public Spliterator spliteratorUndirected() { + int end = blocksCount; + return new FilteredSizedEdgeSpliterator(0, end, e -> !isUndirectedToIgnore(e), undirectedSize()); + } + + public Spliterator spliteratorType(int type, boolean undirected) { + int end = blocksCount; + return new FilteredSizedEdgeSpliterator(0, end, + e -> e.getType() == type && (!undirected || !isUndirectedToIgnore(e)), + undirected ? undirectedSize(type) : size(type)); + } + + public Spliterator spliteratorSelfLoop() { + int end = blocksCount; + return new FilteredEdgeSpliterator(0, end, EdgeImpl::isSelfLoop); + } + + protected Spliterator newFilteredSpliterator(java.util.function.Predicate filter) { + int end = blocksCount; + return new FilteredEdgeSpliterator(0, end, filter); + } + + protected Spliterator newFilteredSizedSpliterator(java.util.function.Predicate filter, int size) { + int end = blocksCount; + return new FilteredSizedEdgeSpliterator(0, end, filter, size); + } + public EdgeStoreIterator iteratorUndirected() { return new UndirectedEdgeStoreIterator(); } @@ -1954,4 +2003,202 @@ public void remove() { EdgeStore.this.remove(pointer); } } + + private class EdgeSpliterator implements Spliterator { + + protected final int endBlockExclusive; + protected int blockIndex; + protected int indexInBlock; + protected EdgeImpl[] currentArray; + protected int currentLength; + protected final int expectedVersion; + protected int totalSize; + protected int consumed; + + EdgeSpliterator(int startBlock, int endBlockExclusive) { + this(startBlock, endBlockExclusive, EdgeStore.this.size()); + } + + EdgeSpliterator(int startBlock, int endBlockExclusive, int totalSize) { + this.blockIndex = startBlock; + this.endBlockExclusive = endBlockExclusive; + this.expectedVersion = version != null ? version.getEdgeVersion() : 0; + this.consumed = 0; + + // Use the total store size for the root spliterator (covering all blocks) + if (startBlock == 0 && endBlockExclusive == blocksCount) { + this.totalSize = totalSize; + } else { + // For split spliterators, compute proportionally + this.totalSize = computeExactSize(startBlock, endBlockExclusive); + } + + if (startBlock < endBlockExclusive) { + EdgeStore.EdgeBlock b = blocks[startBlock]; + currentArray = b.backingArray; + currentLength = b.nodeLength; + indexInBlock = 0; + } else { + currentArray = null; + currentLength = 0; + indexInBlock = 0; + } + } + + private int computeExactSize(int start, int end) { + int sum = 0; + for (int i = start; i < end; i++) { + EdgeStore.EdgeBlock b = blocks[i]; + if (b != null) { + // Exact count: nodeLength minus garbageLength + sum += (b.nodeLength - b.garbageLength); + } + } + return sum; + } + + protected void advanceBlock() { + blockIndex++; + if (blockIndex < endBlockExclusive) { + EdgeStore.EdgeBlock b = blocks[blockIndex]; + currentArray = b.backingArray; + currentLength = b.nodeLength; + indexInBlock = 0; + } else { + currentArray = null; + currentLength = 0; + indexInBlock = 0; + } + } + + protected void checkForComodification() { + if (version != null && expectedVersion != version.getEdgeVersion()) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean tryAdvance(Consumer action) { + checkForComodification(); + while (currentArray != null) { + while (indexInBlock < currentLength) { + EdgeImpl n = currentArray[indexInBlock++]; + if (n != null) { + consumed++; + action.accept(n); + return true; + } + } + advanceBlock(); + } + return false; + } + + protected EdgeSpliterator createSplit(int startBlock, int endBlockExclusive) { + return new EdgeSpliterator(startBlock, endBlockExclusive); + } + + @Override + public Spliterator trySplit() { + // Only split at block boundaries to preserve encounter order + if (indexInBlock != 0) { + return null; + } + + int currentPos = blockIndex; + int remainingBlocks = endBlockExclusive - currentPos; + + if (remainingBlocks <= 1) { + return null; + } + + int mid = currentPos + remainingBlocks / 2; + + // Create left half + EdgeSpliterator left = createSplit(currentPos, mid); + + // Update this spliterator to become the right half + blockIndex = mid; + if (mid < endBlockExclusive) { + EdgeStore.EdgeBlock b = blocks[mid]; + currentArray = b.backingArray; + currentLength = b.nodeLength; + indexInBlock = 0; + } else { + currentArray = null; + currentLength = 0; + indexInBlock = 0; + } + + // Update this spliterator size + this.totalSize = totalSize - left.totalSize; + + return left; + } + + @Override + public long estimateSize() { + // Use the exact totalSize minus what we've consumed + long remaining = totalSize - consumed; + return remaining < 0 ? 0 : remaining; + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SIZED | Spliterator.SUBSIZED; + } + } + + private class FilteredSizedEdgeSpliterator extends EdgeSpliterator { + + protected final Predicate filter; + + FilteredSizedEdgeSpliterator(int startBlock, int endBlockExclusive, Predicate filter, int totalSize) { + super(startBlock, endBlockExclusive, totalSize); + this.filter = filter; + } + + @Override + public boolean tryAdvance(Consumer action) { + checkForComodification(); + while (currentArray != null) { + while (indexInBlock < currentLength) { + EdgeImpl n = currentArray[indexInBlock++]; + if (n != null && filter.test(n)) { + consumed++; + action.accept(n); + return true; + } + } + advanceBlock(); + } + return false; + } + + protected EdgeSpliterator createSplit(int startBlock, int endBlockExclusive) { + return new FilteredSizedEdgeSpliterator(startBlock, endBlockExclusive, filter, totalSize); + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SIZED; + } + } + + private final class FilteredEdgeSpliterator extends FilteredSizedEdgeSpliterator { + + FilteredEdgeSpliterator(int startBlock, int endBlockExclusive, Predicate filter) { + super(startBlock, endBlockExclusive, filter, EdgeStore.this.size()); + } + + protected EdgeSpliterator createSplit(int startBlock, int endBlockExclusive) { + return new FilteredEdgeSpliterator(startBlock, endBlockExclusive, filter); + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL; + } + } + } diff --git a/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java b/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java index 596fa89b..3f2a7fcd 100644 --- a/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/ElementIterableWrapper.java @@ -15,55 +15,84 @@ */ package org.gephi.graph.impl; -import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import org.gephi.graph.api.Element; import org.gephi.graph.api.ElementIterable; public abstract class ElementIterableWrapper implements ElementIterable { - protected final Iterator iterator; + protected final Supplier> iteratorSupplier; + protected final Supplier> spliteratorSupplier; protected final GraphLockImpl lock; + protected final boolean parallelPossible; - public ElementIterableWrapper(Iterator iterator) { - this(iterator, null); + public ElementIterableWrapper(Supplier> iteratorSupplier, GraphLockImpl lock) { + this.iteratorSupplier = iteratorSupplier; + this.spliteratorSupplier = () -> Spliterators + .spliteratorUnknownSize(iteratorSupplier.get(), Spliterator.ORDERED | Spliterator.NONNULL); + this.lock = lock; + this.parallelPossible = false; } - public ElementIterableWrapper(Iterator iterator, GraphLockImpl lock) { - this.iterator = iterator; + public ElementIterableWrapper(Supplier> iteratorSupplier, Supplier> spliteratorSupplier, GraphLockImpl lock) { + this.iteratorSupplier = iteratorSupplier; + this.spliteratorSupplier = spliteratorSupplier; this.lock = lock; + this.parallelPossible = true; } @Override public Iterator iterator() { - return iterator; + return iteratorSupplier.get(); } - protected T[] toArray(T[] a) { - // TODO This can be improved - return toCollection().toArray(a); + @Override + public Spliterator spliterator() { + return spliteratorSupplier.get(); } + @Override + public Stream parallelStream() { + if (!parallelPossible) { + throw new UnsupportedOperationException("Parallel stream not supported for this operation."); + } + return ElementIterable.super.parallelStream(); + } + + public abstract T[] toArray(); + @Override public Collection toCollection() { - List list = new ArrayList<>(); - while (iterator.hasNext()) { - list.add(iterator.next()); + if (parallelPossible && lock != null) { + lock.readLock(); + try { + return StreamSupport.stream(spliterator(), true).collect(Collectors.toList()); + } finally { + lock.readUnlock(); + } } - return list; + return StreamSupport.stream(spliterator(), parallelPossible).collect(Collectors.toList()); } @Override public Set toSet() { - Set set = new HashSet<>(); - while (iterator.hasNext()) { - set.add(iterator.next()); + if (parallelPossible && lock != null) { + lock.readLock(); + try { + return StreamSupport.stream(spliterator(), true).collect(Collectors.toSet()); + } finally { + lock.readUnlock(); + } } - return set; + return StreamSupport.stream(spliterator(), parallelPossible).collect(Collectors.toSet()); } @Override diff --git a/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java b/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java index 9737c4cb..6a3bef35 100644 --- a/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java @@ -17,7 +17,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectList; -import java.util.Collections; +import it.unimi.dsi.fastutil.objects.ObjectLists; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Graph; @@ -196,7 +196,8 @@ public GraphDiffImpl() { @Override public NodeIterable getAddedNodes() { if (!addedNodes.isEmpty()) { - return graphStore.getNodeIterableWrapper(Collections.unmodifiableList(addedNodes).iterator(), false); + return new NodeIterableWrapper(() -> ObjectLists.unmodifiable(addedNodes).iterator(), + () -> ObjectLists.unmodifiable(addedNodes).spliterator(), null); } return NodeIterable.EMPTY; } @@ -204,7 +205,8 @@ public NodeIterable getAddedNodes() { @Override public NodeIterable getRemovedNodes() { if (!removedNodes.isEmpty()) { - return graphStore.getNodeIterableWrapper(Collections.unmodifiableList(removedNodes).iterator(), false); + return new NodeIterableWrapper(() -> ObjectLists.unmodifiable(removedNodes).iterator(), + () -> ObjectLists.unmodifiable(removedNodes).spliterator(), null); } return NodeIterable.EMPTY; } @@ -212,7 +214,8 @@ public NodeIterable getRemovedNodes() { @Override public EdgeIterable getAddedEdges() { if (!addedEdges.isEmpty()) { - return graphStore.getEdgeIterableWrapper(Collections.unmodifiableList(addedEdges).iterator(), false); + return new EdgeIterableWrapper(() -> ObjectLists.unmodifiable(addedEdges).iterator(), + () -> ObjectLists.unmodifiable(addedEdges).spliterator(), null); } return EdgeIterable.EMPTY; } @@ -220,7 +223,8 @@ public EdgeIterable getAddedEdges() { @Override public EdgeIterable getRemovedEdges() { if (!removedEdges.isEmpty()) { - return graphStore.getEdgeIterableWrapper(Collections.unmodifiableList(removedEdges).iterator(), false); + return new EdgeIterableWrapper(() -> ObjectLists.unmodifiable(removedEdges).iterator(), + () -> ObjectLists.unmodifiable(removedEdges).spliterator(), null); } return EdgeIterable.EMPTY; } diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 3d8b015c..0bb033a1 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -19,12 +19,27 @@ import java.time.ZoneId; import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; - -import org.gephi.graph.api.*; +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.DirectedGraph; +import org.gephi.graph.api.DirectedSubgraph; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeIterable; +import org.gephi.graph.api.ElementIterable; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeIterable; +import org.gephi.graph.api.Origin; +import org.gephi.graph.api.SpatialIndex; +import org.gephi.graph.api.Subgraph; +import org.gephi.graph.api.Table; +import org.gephi.graph.api.TimeFormat; +import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.TimestampSet; @@ -248,12 +263,13 @@ protected ElementIterable getElements(Table table) { @Override public EdgeIterable getEdges(int type) { - return new EdgeIterableWrapper(edgeStore.iteratorType(type, false)); + return new EdgeIterableWrapper(() -> edgeStore.iteratorType(type, false), + () -> edgeStore.spliteratorType(type, false), getAutoLock()); } @Override public EdgeIterable getSelfLoops() { - return new EdgeIterableWrapper(edgeStore.iteratorSelfLoop()); + return new EdgeIterableWrapper(edgeStore::iteratorSelfLoop, edgeStore::spliteratorSelfLoop, getAutoLock()); } @Override @@ -261,8 +277,7 @@ public boolean removeNode(final Node node) { autoWriteLock(); try { nodeStore.checkNonNullNodeObject(node); - for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator((NodeImpl) node); edgeIterator - .hasNext();) { + for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(node); edgeIterator.hasNext();) { edgeIterator.next(); edgeIterator.remove(); } @@ -288,8 +303,7 @@ public boolean removeAllNodes(Collection nodes) { try { for (Node node : nodes) { nodeStore.checkNonNullNodeObject(node); - for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator((NodeImpl) node); edgeIterator - .hasNext();) { + for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(node); edgeIterator.hasNext();) { edgeIterator.next(); edgeIterator.remove(); } @@ -370,11 +384,7 @@ public Edge getEdge(final Node node1, final Node node2, final int type) { @Override public EdgeIterable getEdges(Node node1, Node node2, int type) { - Iterator itr = edgeStore.getAll(node1, node2, type, false); - if (itr != null) { - return new EdgeIterableWrapper(itr); - } - return EdgeIterable.EMPTY; + return new EdgeIterableWrapper(() -> edgeStore.getAll(node1, node2, type, false), getAutoLock()); } @Override @@ -389,71 +399,67 @@ public Edge getEdge(final Node node1, final Node node2) { @Override public EdgeIterable getEdges(Node node1, Node node2) { - Iterator itr = edgeStore.getAll(node1, node2, false); - if (itr != null) { - return new EdgeIterableWrapper(itr); - } - return EdgeIterable.EMPTY; + return new EdgeIterableWrapper(() -> edgeStore.getAll(node1, node2, false), getAutoLock()); } @Override public NodeIterable getNeighbors(final Node node) { - return new NodeIterableWrapper(edgeStore.neighborIterator(node)); + return new NodeIterableWrapper(() -> edgeStore.neighborIterator(node), getAutoLock()); } @Override public NodeIterable getNeighbors(final Node node, final int type) { - return new NodeIterableWrapper(edgeStore.neighborIterator(node, type)); + return new NodeIterableWrapper(() -> edgeStore.neighborIterator(node, type), getAutoLock()); } @Override public NodeIterable getPredecessors(final Node node) { - return new NodeIterableWrapper(edgeStore.neighborInIterator(node)); + return new NodeIterableWrapper(() -> edgeStore.neighborInIterator(node), getAutoLock()); } @Override public NodeIterable getPredecessors(final Node node, final int type) { - return new NodeIterableWrapper(edgeStore.neighborInIterator(node, type)); + return new NodeIterableWrapper(() -> edgeStore.neighborInIterator(node, type), getAutoLock()); } @Override public NodeIterable getSuccessors(final Node node) { - return new NodeIterableWrapper(edgeStore.neighborOutIterator(node)); + return new NodeIterableWrapper(() -> edgeStore.neighborOutIterator(node), getAutoLock()); } @Override public NodeIterable getSuccessors(final Node node, final int type) { - return new NodeIterableWrapper(edgeStore.neighborOutIterator(node, type)); + return new NodeIterableWrapper(() -> edgeStore.neighborOutIterator(node, type), getAutoLock()); } @Override public EdgeIterable getEdges(final Node node) { - return new EdgeIterableWrapper(edgeStore.edgeIterator(node)); + return new EdgeIterableWrapper(() -> edgeStore.edgeIterator(node), getAutoLock()); } @Override public EdgeIterable getEdges(final Node node, final int type) { - return new EdgeIterableWrapper(edgeStore.edgeIterator(node, type)); + return new EdgeIterableWrapper(() -> edgeStore.edgeIterator(node, type), getAutoLock()); } @Override public EdgeIterable getInEdges(final Node node) { - return new EdgeIterableWrapper(edgeStore.edgeInIterator(node)); + return new EdgeIterableWrapper(() -> edgeStore.edgeInIterator(node), getAutoLock()); } @Override public EdgeIterable getInEdges(final Node node, final int type) { - return new EdgeIterableWrapper(edgeStore.edgeInIterator(node, type)); + return new EdgeIterableWrapper(() -> edgeStore.edgeInIterator(node, type), getAutoLock()); } @Override public EdgeIterable getOutEdges(final Node node) { - return new EdgeIterableWrapper(edgeStore.edgeOutIterator(node)); + return new EdgeIterableWrapper(() -> edgeStore.edgeOutIterator(node), getAutoLock()); } @Override public EdgeIterable getOutEdges(final Node node, final int type) { - return new EdgeIterableWrapper(edgeStore.edgeOutIterator(node, type)); + return new EdgeIterableWrapper(() -> edgeStore.edgeOutIterator(node, type), getAutoLock()); } @Override @@ -833,20 +839,8 @@ protected void destroyGraphObserver(GraphObserverImpl observer) { } } - protected EdgeIterableWrapper getEdgeIterableWrapper(Iterator edgeIterator) { - return getEdgeIterableWrapper(edgeIterator, true); - } - - protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator) { - return getNodeIterableWrapper(nodeIterator, true); - } - - protected EdgeIterableWrapper getEdgeIterableWrapper(Iterator edgeIterator, boolean blocking) { - return new EdgeIterableWrapper(edgeIterator, (blocking && configuration.isEnableAutoLocking()) ? lock : null); - } - - protected NodeIterableWrapper getNodeIterableWrapper(Iterator nodeIterator, boolean blocking) { - return new NodeIterableWrapper(nodeIterator, (blocking && configuration.isEnableAutoLocking()) ? lock : null); + protected GraphLockImpl getAutoLock() { + return configuration.isEnableAutoLocking() ? lock : null; } public int deepHashCode() { diff --git a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java index e57632a2..02a4e0f4 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java +++ b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java @@ -35,9 +35,9 @@ public final class GraphStoreConfiguration { public static final boolean DEFAULT_ENABLE_EDGE_WEIGHT_COLUMN = true; public static final boolean DEFAULT_ENABLE_PARALLEL_EDGES_SAME_TYPE = true; // NodeStore - public final static int NODESTORE_BLOCK_SIZE = 5000; - public final static int NODESTORE_DEFAULT_BLOCKS = 10; - public static final int NODESTORE_DEFAULT_DICTIONARY_SIZE = 1000; + public final static int NODESTORE_BLOCK_SIZE = 8192; + public final static int NODESTORE_DEFAULT_BLOCKS = 5; + public static final int NODESTORE_DEFAULT_DICTIONARY_SIZE = 8192; public final static float NODESTORE_DICTIONARY_LOAD_FACTOR = .7f; // EdgeStore public static final int EDGESTORE_BLOCK_SIZE = 8192; diff --git a/src/main/java/org/gephi/graph/impl/GraphVersion.java b/src/main/java/org/gephi/graph/impl/GraphVersion.java index d436325e..fcb717e4 100644 --- a/src/main/java/org/gephi/graph/impl/GraphVersion.java +++ b/src/main/java/org/gephi/graph/impl/GraphVersion.java @@ -45,6 +45,14 @@ public int incrementAndGetEdgeVersion() { return edgeVersion; } + public int getNodeVersion() { + return nodeVersion; + } + + public int getEdgeVersion() { + return edgeVersion; + } + private void handleNodeReset() { if (graph != null) { if (graph.getView().isMainView()) { diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 79971b30..741016c5 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -18,6 +18,9 @@ import java.util.Collection; import java.util.Iterator; import java.util.Set; +import java.util.Spliterator; +import java.util.ConcurrentModificationException; +import java.util.function.Consumer; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; @@ -60,8 +63,9 @@ public Edge getEdge(Node node1, Node node2) { @Override public EdgeIterable getEdges(Node node1, Node node2) { - return graphStore - .getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.getAll(node1, node2, undirected))); + return new EdgeIterableWrapper( + () -> new EdgeViewIterator(graphStore.edgeStore.getAll(node1, node2, undirected)), + graphStore.getAutoLock()); } @Override @@ -80,8 +84,9 @@ public Edge getEdge(Node node1, Node node2, int type) { @Override public EdgeIterable getEdges(Node node1, Node node2, int type) { - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator( - graphStore.edgeStore.getAll(node1, node2, type, undirected))); + return new EdgeIterableWrapper( + () -> new EdgeViewIterator(graphStore.edgeStore.getAll(node1, node2, type, undirected)), + graphStore.getAutoLock()); } @Override @@ -101,54 +106,61 @@ public Edge getMutualEdge(Edge e) { @Override public NodeIterable getPredecessors(Node node) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, - new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node)))); + return new NodeIterableWrapper(() -> new NeighborsIterator((NodeImpl) node, + new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node))), graphStore.getAutoLock()); } @Override public NodeIterable getPredecessors(Node node, int type) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, - new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node, type)))); + return new NodeIterableWrapper( + () -> new NeighborsIterator((NodeImpl) node, + new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node, type))), + graphStore.getAutoLock()); } @Override public NodeIterable getSuccessors(Node node) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, - new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node)))); + return new NodeIterableWrapper(() -> new NeighborsIterator((NodeImpl) node, + new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node))), graphStore.getAutoLock()); } @Override public NodeIterable getSuccessors(Node node, int type) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, - new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node, type)))); + return new NodeIterableWrapper( + () -> new NeighborsIterator((NodeImpl) node, + new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node, type))), + graphStore.getAutoLock()); } @Override public EdgeIterable getInEdges(Node node) { checkValidInViewNodeObject(node); - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node))); + return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node)), + graphStore.getAutoLock()); } @Override public EdgeIterable getInEdges(Node node, int type) { checkValidInViewNodeObject(node); - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node, type))); + return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.edgeInIterator(node, type)), + graphStore.getAutoLock()); } @Override public EdgeIterable getOutEdges(Node node) { checkValidInViewNodeObject(node); - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node))); + return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node)), + graphStore.getAutoLock()); } @Override public EdgeIterable getOutEdges(Node node, int type) { checkValidInViewNodeObject(node); - return graphStore - .getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node, type))); + return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.edgeOutIterator(node, type)), + graphStore.getAutoLock()); } @Override @@ -372,51 +384,78 @@ public boolean hasEdge(final Object id) { @Override public NodeIterable getNodes() { - return graphStore.getNodeIterableWrapper(new NodeViewIterator(graphStore.nodeStore.iterator())); + return new NodeIterableWrapper(() -> new NodeViewIterator(graphStore.nodeStore.iterator()), + NodeViewSpliterator::new, graphStore.getAutoLock()); } @Override public EdgeIterable getEdges() { if (undirected) { - return graphStore.getEdgeIterableWrapper(new UndirectedEdgeViewIterator(graphStore.edgeStore.iterator())); + return new EdgeIterableWrapper(() -> new UndirectedEdgeViewIterator(graphStore.edgeStore.iterator()), + () -> graphStore.edgeStore + .newFilteredSizedSpliterator(e -> view.containsEdge(e) && !isUndirectedToIgnore(e), view + .getUndirectedEdgeCount()), + graphStore.getAutoLock()); } else { - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.iterator())); + return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.iterator()), + () -> graphStore.edgeStore.newFilteredSizedSpliterator(view::containsEdge, view.getEdgeCount()), + graphStore.getAutoLock()); } } @Override public EdgeIterable getEdges(int type) { - return graphStore.getEdgeIterableWrapper(new UndirectedEdgeViewIterator( - graphStore.edgeStore.iteratorType(type, undirected))); + if (undirected) { + return new EdgeIterableWrapper( + () -> new UndirectedEdgeViewIterator(graphStore.edgeStore.iteratorType(type, undirected)), + () -> graphStore.edgeStore.newFilteredSizedSpliterator(e -> e.getType() == type && view + .containsEdge(e) && !isUndirectedToIgnore(e), view.getUndirectedEdgeCount(type)), + graphStore.getAutoLock()); + } else { + return new EdgeIterableWrapper( + () -> new EdgeViewIterator(graphStore.edgeStore.iteratorType(type, undirected)), + () -> graphStore.edgeStore + .newFilteredSizedSpliterator(e -> e.getType() == type && view.containsEdge(e), view + .getEdgeCount(type)), + graphStore.getAutoLock()); + } } @Override public EdgeIterable getSelfLoops() { - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.iteratorSelfLoop())); + return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.iteratorSelfLoop()), + () -> graphStore.edgeStore.newFilteredSpliterator(e -> e.isSelfLoop() && view.containsEdge(e)), + graphStore.getAutoLock()); } @Override public NodeIterable getNeighbors(Node node) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, - new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node)))); + return new NodeIterableWrapper( + () -> new NeighborsIterator((NodeImpl) node, + new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node))), + graphStore.getAutoLock()); } @Override public NodeIterable getNeighbors(Node node, int type) { checkValidInViewNodeObject(node); - return graphStore.getNodeIterableWrapper(new NeighborsIterator((NodeImpl) node, - new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node, type)))); + return new NodeIterableWrapper( + () -> new NeighborsIterator((NodeImpl) node, + new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node, type))), + graphStore.getAutoLock()); } @Override public EdgeIterable getEdges(Node node) { checkValidInViewNodeObject(node); if (undirected) { - return graphStore - .getEdgeIterableWrapper(new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node))); + return new EdgeIterableWrapper( + () -> new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node)), + graphStore.getAutoLock()); } else { - return graphStore.getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.edgeIterator(node))); + return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.edgeIterator(node)), + graphStore.getAutoLock()); } } @@ -424,13 +463,13 @@ public EdgeIterable getEdges(Node node) { public EdgeIterable getEdges(Node node, int type) { checkValidInViewNodeObject(node); if (undirected) { - return graphStore.getEdgeIterableWrapper(new UndirectedEdgeViewIterator( - graphStore.edgeStore.edgeIterator(node, type))); + return new EdgeIterableWrapper( + () -> new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node, type)), + graphStore.getAutoLock()); } else { - return graphStore - .getEdgeIterableWrapper(new EdgeViewIterator(graphStore.edgeStore.edgeIterator(node, type))); + return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.edgeIterator(node, type)), + graphStore.getAutoLock()); } - } @Override @@ -560,7 +599,7 @@ public void clearEdges(Node node) { graphStore.autoWriteLock(); try { EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node); - for (; itr.hasNext();) { + while (itr.hasNext()) { EdgeImpl edge = itr.next(); view.removeEdge(edge); } @@ -574,7 +613,7 @@ public void clearEdges(Node node, int type) { graphStore.autoWriteLock(); try { EdgeStore.EdgeTypeInOutIterator itr = graphStore.edgeStore.edgeIterator(node, type); - for (; itr.hasNext();) { + while (itr.hasNext()) { EdgeImpl edge = itr.next(); view.removeEdge(edge); } @@ -832,8 +871,9 @@ public NodeIterable getNodesInArea(Rect2D rect) { if (graphStore.spatialIndex == null) { throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); } - Iterator iterator = graphStore.spatialIndex.getNodesInArea(rect).iterator(); - return new NodeIterableWrapper(new NodeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); + return new NodeIterableWrapper( + () -> new NodeViewIterator(graphStore.spatialIndex.getNodesInArea(rect).iterator()), + graphStore.spatialIndex.nodesTree.lock); } @Override @@ -841,8 +881,9 @@ public EdgeIterable getEdgesInArea(Rect2D rect) { if (graphStore.spatialIndex == null) { throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); } - Iterator iterator = graphStore.spatialIndex.getEdgesInArea(rect).iterator(); - return new EdgeIterableWrapper(new EdgeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); + return new EdgeIterableWrapper( + () -> new EdgeViewIterator(graphStore.spatialIndex.getEdgesInArea(rect).iterator()), + graphStore.spatialIndex.nodesTree.lock); } @Override @@ -885,6 +926,155 @@ public Rect2D getBoundaries() { } } + private final class NodeViewSpliterator implements Spliterator { + + private final int endBlockExclusive; + private int blockIndex; + private int indexInBlock; + private NodeImpl[] currentArray; + private int currentLength; + private final int expectedVersion; + private int totalSize; + private int consumed; + + NodeViewSpliterator() { + this(0, graphStore.nodeStore.blocksCount); + } + + NodeViewSpliterator(int startBlock, int endBlockExclusive) { + this.blockIndex = startBlock; + this.endBlockExclusive = endBlockExclusive; + this.expectedVersion = graphStore.version != null ? graphStore.version.getNodeVersion() : 0; + this.consumed = 0; + + // Use the view's node count for exact sizing + // Use the total store size for the root spliterator (covering all blocks) + if (startBlock == 0 && endBlockExclusive == graphStore.nodeStore.blocksCount) { + this.totalSize = view.getNodeCount(); + } else { + // For split spliterators, compute proportionally + this.totalSize = computeSizeEstimate(startBlock, endBlockExclusive); + } + + if (startBlock < endBlockExclusive) { + NodeStore.NodeBlock b = graphStore.nodeStore.blocks[startBlock]; + currentArray = b.backingArray; + currentLength = b.nodeLength; + indexInBlock = 0; + } else { + currentArray = null; + currentLength = 0; + indexInBlock = 0; + } + } + + private void advanceBlock() { + blockIndex++; + if (blockIndex < endBlockExclusive) { + NodeStore.NodeBlock b = graphStore.nodeStore.blocks[blockIndex]; + currentArray = b.backingArray; + currentLength = b.nodeLength; + indexInBlock = 0; + } else { + currentArray = null; + currentLength = 0; + indexInBlock = 0; + } + } + + private void checkForComodification() { + if (graphStore.version != null && expectedVersion != graphStore.version.getNodeVersion()) { + throw new ConcurrentModificationException(); + } + } + + private int computeSizeEstimate(int start, int end) { + int sum = 0; + for (int i = start; i < end; i++) { + NodeStore.NodeBlock b = graphStore.nodeStore.blocks[i]; + if (b != null) { + // Exact count: nodeLength minus garbageLength + sum += (b.nodeLength - b.garbageLength); + } + } + if (sum > 0) { + // Scale by view ratio to estimate number of nodes in view + double viewRatio = (double) view.getNodeCount() / graphStore.nodeStore.size; + sum = (int) Math.round(sum * viewRatio); + } + return sum; + } + + @Override + public boolean tryAdvance(Consumer action) { + checkForComodification(); + while (currentArray != null) { + while (indexInBlock < currentLength) { + NodeImpl n = currentArray[indexInBlock++]; + if (n != null && view.containsNode(n)) { + consumed++; + action.accept(n); + return true; + } + } + advanceBlock(); + } + return false; + } + + @Override + public Spliterator trySplit() { + // Only split at block boundaries to preserve encounter order + if (indexInBlock != 0) { + return null; + } + + int currentPos = blockIndex; + int remainingBlocks = endBlockExclusive - currentPos; + + if (remainingBlocks <= 1) { + return null; + } + + int mid = currentPos + remainingBlocks / 2; + + // Create left half + NodeViewSpliterator left = new NodeViewSpliterator(currentPos, mid); + + // Update this spliterator to become the right half + blockIndex = mid; + if (mid < endBlockExclusive) { + NodeStore.NodeBlock b = graphStore.nodeStore.blocks[mid]; + currentArray = b.backingArray; + currentLength = b.nodeLength; + indexInBlock = 0; + } else { + currentArray = null; + currentLength = 0; + indexInBlock = 0; + } + + // Update this spliterator size + this.totalSize = Math.max(0, totalSize - left.totalSize); + + return left; + } + + @Override + public long estimateSize() { + // Use the exact view size minus what we've consumed + long remaining = totalSize - consumed; + return remaining < 0 ? 0 : remaining; + } + + @Override + public int characteristics() { + // SIZED because we know the exact count from view.getNodeCount() + // But not SUBSIZED because splits can't guarantee exact size distribution + return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SIZED; + } + } + protected final class NodeViewIterator implements Iterator { private final Iterator nodeIterator; @@ -989,7 +1179,7 @@ public void remove() { } } - protected class NeighborsIterator implements Iterator { + protected static class NeighborsIterator implements Iterator { protected final NodeImpl node; protected final Iterator itr; diff --git a/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java b/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java index d8cb4c79..dab1e7b9 100644 --- a/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java +++ b/src/main/java/org/gephi/graph/impl/NodeIterableWrapper.java @@ -16,21 +16,32 @@ package org.gephi.graph.impl; import java.util.Iterator; +import java.util.Spliterator; +import java.util.function.Supplier; +import java.util.stream.StreamSupport; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; public class NodeIterableWrapper extends ElementIterableWrapper implements NodeIterable { - public NodeIterableWrapper(Iterator iterator) { - super(iterator); + public NodeIterableWrapper(Supplier> iteratorSupplier, GraphLockImpl lock) { + super(iteratorSupplier, lock); } - public NodeIterableWrapper(Iterator iterator, GraphLockImpl lock) { - super(iterator, lock); + public NodeIterableWrapper(Supplier> iteratorSupplier, Supplier> spliteratorSupplier, GraphLockImpl lock) { + super(iteratorSupplier, spliteratorSupplier, lock); } @Override public Node[] toArray() { - return toArray(new Node[0]); + if (parallelPossible && lock != null) { + lock.readLock(); + try { + return StreamSupport.stream(spliterator(), true).toArray(Node[]::new); + } finally { + lock.readUnlock(); + } + } + return StreamSupport.stream(spliterator(), parallelPossible).toArray(Node[]::new); } } diff --git a/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java index 709deb31..0ec778c9 100644 --- a/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -21,10 +21,15 @@ import it.unimi.dsi.fastutil.objects.ObjectSet; import java.util.ArrayList; import java.util.Collection; +import java.util.ConcurrentModificationException; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; @@ -46,7 +51,7 @@ public class NodeStore implements Collection, NodeIterable { protected int garbageSize; protected int blocksCount; protected int currentBlockIndex; - protected NodeBlock blocks[]; + protected NodeBlock[] blocks; protected NodeBlock currentBlock; protected Object2IntOpenHashMap dictionary; @@ -174,6 +179,22 @@ public NodeStoreIterator iterator() { return new NodeStoreIterator(); } + @Override + public Spliterator spliterator() { + int end = blocksCount; + return new NodeSpliterator(0, end); + } + + @Override + public Stream stream() { + return StreamSupport.stream(spliterator(), false); + } + + @Override + public Stream parallelStream() { + return StreamSupport.stream(spliterator(), true); + } + @Override public NodeImpl[] toArray() { readLock(); @@ -675,4 +696,141 @@ public void remove() { NodeStore.this.remove(pointer); } } + + private final class NodeSpliterator implements Spliterator { + + private final int endBlockExclusive; + private int blockIndex; + private int indexInBlock; + private NodeImpl[] currentArray; + private int currentLength; + private final int expectedVersion; + private int totalSize; + private int consumed; + + NodeSpliterator(int startBlock, int endBlockExclusive) { + this.blockIndex = startBlock; + this.endBlockExclusive = endBlockExclusive; + this.expectedVersion = version != null ? version.getNodeVersion() : 0; + this.consumed = 0; + + // Use the total store size for the root spliterator (covering all blocks) + if (startBlock == 0 && endBlockExclusive == blocksCount) { + this.totalSize = NodeStore.this.size(); + } else { + // For split spliterators, compute proportionally + this.totalSize = computeExactSize(startBlock, endBlockExclusive); + } + + if (startBlock < endBlockExclusive) { + NodeBlock b = blocks[startBlock]; + currentArray = b.backingArray; + currentLength = b.nodeLength; + indexInBlock = 0; + } else { + currentArray = null; + currentLength = 0; + indexInBlock = 0; + } + } + + private int computeExactSize(int start, int end) { + int sum = 0; + for (int i = start; i < end; i++) { + NodeBlock b = blocks[i]; + if (b != null) { + // Exact count: nodeLength minus garbageLength + sum += (b.nodeLength - b.garbageLength); + } + } + return sum; + } + + private void advanceBlock() { + blockIndex++; + if (blockIndex < endBlockExclusive) { + NodeBlock b = blocks[blockIndex]; + currentArray = b.backingArray; + currentLength = b.nodeLength; + indexInBlock = 0; + } else { + currentArray = null; + currentLength = 0; + indexInBlock = 0; + } + } + + private void checkForComodification() { + if (version != null && expectedVersion != version.getNodeVersion()) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean tryAdvance(Consumer action) { + checkForComodification(); + while (currentArray != null) { + while (indexInBlock < currentLength) { + NodeImpl n = currentArray[indexInBlock++]; + if (n != null) { + consumed++; + action.accept(n); + return true; + } + } + advanceBlock(); + } + return false; + } + + @Override + public Spliterator trySplit() { + // Only split at block boundaries to preserve encounter order + if (indexInBlock != 0) { + return null; + } + + int currentPos = blockIndex; + int remainingBlocks = endBlockExclusive - currentPos; + + if (remainingBlocks <= 1) { + return null; + } + + int mid = currentPos + remainingBlocks / 2; + + // Create left half + NodeSpliterator left = new NodeSpliterator(currentPos, mid); + + // Update this spliterator to become the right half + blockIndex = mid; + if (mid < endBlockExclusive) { + NodeBlock b = blocks[mid]; + currentArray = b.backingArray; + currentLength = b.nodeLength; + indexInBlock = 0; + } else { + currentArray = null; + currentLength = 0; + indexInBlock = 0; + } + + // Update this spliterator size + this.totalSize = totalSize - left.totalSize; + + return left; + } + + @Override + public long estimateSize() { + // Use the exact totalSize minus what we've consumed + long remaining = totalSize - consumed; + return remaining < 0 ? 0 : remaining; + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SIZED | Spliterator.SUBSIZED; + } + } } diff --git a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java index 75389855..1a4152b4 100644 --- a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java @@ -31,7 +31,8 @@ public NodeIterable getNodesInArea(Rect2D rect) { @Override public EdgeIterable getEdgesInArea(Rect2D rect) { - return new EdgeIterableWrapper(new EdgeIterator(rect, nodesTree.getNodes(rect).iterator()), nodesTree.lock); + return new EdgeIterableWrapper(() -> new EdgeIterator(rect, nodesTree.getNodes(rect).iterator()), + nodesTree.lock); } protected void clearNodes() { diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 91bc601e..055a4d5e 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -17,8 +17,18 @@ import java.util.Collection; import java.util.Set; - -import org.gephi.graph.api.*; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeIterable; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeIterable; +import org.gephi.graph.api.SpatialIndex; +import org.gephi.graph.api.Subgraph; +import org.gephi.graph.api.UndirectedGraph; +import org.gephi.graph.api.UndirectedSubgraph; public class UndirectedDecorator implements UndirectedGraph, UndirectedSubgraph { @@ -138,7 +148,8 @@ public Edge getEdge(Node node1, Node node2) { @Override public EdgeIterable getEdges(Node node1, Node node2) { - return store.getEdgeIterableWrapper(store.edgeStore.edgesUndirectedIterator(node1, node2)); + return new EdgeIterableWrapper(() -> store.edgeStore.edgesUndirectedIterator(node1, node2), + store.getAutoLock()); } @Override @@ -153,7 +164,8 @@ public Edge getEdge(Node node1, Node node2, int type) { @Override public EdgeIterable getEdges(Node node1, Node node2, int type) { - return store.getEdgeIterableWrapper(store.edgeStore.edgesUndirectedIterator(node1, node2, type)); + return new EdgeIterableWrapper(() -> store.edgeStore.edgesUndirectedIterator(node1, node2, type), + store.getAutoLock()); } @Override @@ -163,37 +175,40 @@ public NodeIterable getNodes() { @Override public EdgeIterable getEdges() { - return store.getEdgeIterableWrapper(store.edgeStore.iteratorUndirected()); + return new EdgeIterableWrapper(store.edgeStore::iteratorUndirected, store.edgeStore::spliteratorUndirected, + store.getAutoLock()); } @Override public EdgeIterable getEdges(int type) { - return store.getEdgeIterableWrapper(store.edgeStore.iteratorType(type, true)); + return new EdgeIterableWrapper(() -> store.edgeStore.iteratorType(type, true), + () -> store.edgeStore.spliteratorType(type, true), store.getAutoLock()); } @Override public EdgeIterable getSelfLoops() { - return store.getEdgeIterableWrapper(store.edgeStore.iteratorSelfLoop()); + return new EdgeIterableWrapper(store.edgeStore::iteratorSelfLoop, store.edgeStore::spliteratorSelfLoop, + store.getAutoLock()); } @Override public NodeIterable getNeighbors(Node node) { - return store.getNodeIterableWrapper(store.edgeStore.neighborIterator(node)); + return new NodeIterableWrapper(() -> store.edgeStore.neighborIterator(node), store.getAutoLock()); } @Override public NodeIterable getNeighbors(Node node, int type) { - return store.getNodeIterableWrapper(store.edgeStore.neighborIterator(node, type)); + return new NodeIterableWrapper(() -> store.edgeStore.neighborIterator(node, type), store.getAutoLock()); } @Override public EdgeIterable getEdges(Node node) { - return store.getEdgeIterableWrapper(store.edgeStore.edgeUndirectedIterator(node)); + return new EdgeIterableWrapper(() -> store.edgeStore.edgeUndirectedIterator(node), store.getAutoLock()); } @Override public EdgeIterable getEdges(Node node, int type) { - return store.getEdgeIterableWrapper(store.edgeStore.edgeUndirectedIterator(node, type)); + return new EdgeIterableWrapper(() -> store.edgeStore.edgeUndirectedIterator(node, type), store.getAutoLock()); } @Override diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index 17b45f9c..6e17f62b 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -35,7 +35,9 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnIterable; import org.gephi.graph.api.DirectedGraph; @@ -1149,6 +1151,21 @@ public Iterator iterator() { return new BasicNodeIterator(idToNodeMap.values().iterator()); } + @Override + public Spliterator spliterator() { + return Spliterators.spliteratorUnknownSize(iterator(), Spliterator.ORDERED | Spliterator.NONNULL); + } + + @Override + public Stream stream() { + return Collection.super.stream(); + } + + @Override + public Stream parallelStream() { + return Collection.super.parallelStream(); + } + @Override public Node[] toArray() { return idToNodeMap.values().toArray(new Node[0]); diff --git a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java index f6fc2b86..0498dfd3 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java @@ -30,13 +30,18 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.ConcurrentModificationException; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.Set; +import java.util.Spliterator; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Node; import org.testng.Assert; import org.testng.annotations.Test; @@ -1783,7 +1788,7 @@ public void testUndirectedIterator() { } EdgeStore.EdgeStoreIterator undirectedIterator = edgeStore.iteratorUndirected(); - for (; undirectedIterator.hasNext();) { + while (undirectedIterator.hasNext()) { EdgeImpl e = undirectedIterator.next(); Assert.assertTrue(edgeSet.remove(e)); } @@ -1791,6 +1796,141 @@ public void testUndirectedIterator() { Assert.assertEquals(0, edgeSet.size()); } + @Test + public void testUndirectedSpliterator() { + EdgeImpl[] edges = GraphGenerator.generateMutualEdges(0); + EdgeStore edgeStore = new EdgeStore(); + edgeStore.addAll(Arrays.asList(edges)); + + // Collect ids from spliteratorUndirected + Spliterator spliterator = edgeStore.spliteratorUndirected(); + Assert.assertEquals(spliterator.estimateSize(), 1); + List edgeList = StreamSupport.stream(spliterator, true).collect(Collectors.toList()); + Assert.assertEquals(edgeList.size(), 1); + Assert.assertEquals(edgeList.get(0), edges[0]); + } + + @Test + public void testUndirectedSpliteratorMatchesIterator() { + EdgeImpl[] edges = GraphGenerator.generateEdgeList(300, 0, true, true, false); + EdgeStore edgeStore = new EdgeStore(); + edgeStore.addAll(Arrays.asList(edges)); + + // Collect ids from iteratorUndirected + List idsIter = iteratorToArray(edgeStore.iteratorUndirected()); + + // Collect ids from spliteratorUndirected + Spliterator spliterator = edgeStore.spliteratorUndirected(); + Assert.assertEquals(spliterator.estimateSize(), idsIter.size()); + List idsSplit = StreamSupport.stream(spliterator, true).collect(Collectors.toList()); + + Assert.assertEquals(idsSplit, idsIter); + } + + @Test + public void testTypeSpliteratorMatchesIterator() { + EdgeImpl[] edges = GraphGenerator.generateSmallMultiTypeEdgeList(); + EdgeStore edgeStore = new EdgeStore(); + edgeStore.addAll(Arrays.asList(edges)); + + for (boolean directed : new boolean[] { true, false }) { + for (int type = 0; type < 3; type++) { + // Collect ids from iteratorUndirected + List idsIter = iteratorToArray(edgeStore.iteratorType(type, directed)); + + // Collect ids from spliteratorUndirected + Spliterator spliterator = edgeStore.spliteratorType(type, directed); + Assert.assertEquals(spliterator.estimateSize(), idsIter.size()); + List idsSplit = StreamSupport.stream(spliterator, true).collect(Collectors.toList()); + + Assert.assertEquals(idsSplit, idsIter); + } + } + } + + @Test + public void testSelfLoopSpliterator() { + EdgeStore edgeStore = new EdgeStore(); + edgeStore.add(GraphGenerator.generateSelfLoop(0, true)); + + List idsSplit = StreamSupport.stream(edgeStore.spliteratorSelfLoop(), true).collect(Collectors.toList()); + List idsIter = iteratorToArray(edgeStore.iteratorSelfLoop()); + Assert.assertEquals(idsSplit, idsIter); + } + + @Test + public void testFilteredSpliteratorCharacteristicsAndEstimate() { + EdgeImpl[] edges = GraphGenerator.generateEdgeList(200, 0, true, true, false); + EdgeStore edgeStore = new EdgeStore(); + edgeStore.addAll(Arrays.asList(edges)); + + Spliterator sp = edgeStore.spliteratorUndirected(); + int ch = sp.characteristics(); + Assert.assertTrue((ch & Spliterator.SIZED) != 0); + Assert.assertTrue((ch & Spliterator.SUBSIZED) == 0); + + // Count expected + List expected = iteratorToArray(edgeStore.iteratorUndirected()); + Assert.assertEquals(sp.estimateSize(), expected.size()); + + // Consume 5 and check estimate decreases + for (int i = 0; i < 5; i++) { + Assert.assertTrue(sp.tryAdvance(e -> { + })); + } + Assert.assertEquals(sp.estimateSize(), expected.size() - 5); + } + + @Test + public void testEdgeSpliteratorCoversAll() { + NodeStore nodeStore = GraphGenerator.generateNodeStore(2); + NodeImpl n1 = nodeStore.get(0); + NodeImpl n2 = nodeStore.get(1); + EdgeStore store = new EdgeStore(); + int n = GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE * 2 + 321; + for (int i = 0; i < n; i++) { + store.add(new EdgeImpl("e" + i, n1, n2, 0, 1.0, true)); + } + Set seen = new HashSet<>(); + Spliterator sp = store.spliterator(); + sp.forEachRemaining(seen::add); + Assert.assertEquals(seen.size(), n); + for (Edge e : store) { + Assert.assertTrue(seen.contains(e)); + } + } + + @Test(expectedExceptions = ConcurrentModificationException.class) + public void testEdgeSpliteratorFailFastOnAdd() { + EdgeImpl[] edges = GraphGenerator.generateSmallMultiTypeEdgeList(); + EdgeStore edgeStore = new EdgeStore(null, null, null, null, null, new GraphVersion(null)); + edgeStore.addAll(Arrays.asList(edges)); + Spliterator sp = edgeStore.spliterator(); + Assert.assertTrue(edgeStore.remove(edges[0])); + sp.tryAdvance(x -> { + }); + } + + @Test + public void testEdgeParallelStreamCount() { + EdgeImpl[] edges = GraphGenerator.generateEdgeList(100000, 2, true, true, true); + EdgeStore edgeStore = new EdgeStore(); + edgeStore.addAll(Arrays.asList(edges)); + long count = edgeStore.parallelStream().count(); + Assert.assertEquals(count, edges.length); + } + + @Test + public void testEdgeStream() { + EdgeStore store = new EdgeStore(); + EdgeImpl[] edges = GraphGenerator.generateLargeEdgeList(); + store.addAll(Arrays.asList(edges)); + + Set set = Collections.synchronizedSet(new HashSet<>()); + store.parallelStream().forEachOrdered(set::add); + Assert.assertEquals(set, store.toSet()); + } + @Test public void testUndirectedIteratorRemove() { EdgeStore edgeStore = new EdgeStore(); @@ -2105,10 +2245,10 @@ private NodeImpl[] getNodes(EdgeImpl[] edges) { return nodes.toArray(new NodeImpl[0]); } - public List iteratorToArray(Iterator edgeIterator) { - List list = new ArrayList<>(); + public List iteratorToArray(Iterator edgeIterator) { + List list = new ArrayList<>(); for (; edgeIterator.hasNext();) { - EdgeImpl e = edgeIterator.next(); + Edge e = edgeIterator.next(); list.add(e); } return list; diff --git a/src/test/java/org/gephi/graph/impl/EmptyIterableTest.java b/src/test/java/org/gephi/graph/impl/EmptyIterableTest.java index 5650fa94..e33283f4 100644 --- a/src/test/java/org/gephi/graph/impl/EmptyIterableTest.java +++ b/src/test/java/org/gephi/graph/impl/EmptyIterableTest.java @@ -16,8 +16,10 @@ package org.gephi.graph.impl; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.stream.Collectors; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Element; @@ -62,6 +64,15 @@ public void testEdgeIterableDoBreak() { EdgeIterable.EMPTY.doBreak(); } + @Test + public void testEdgeIterableSpliterator() { + EdgeIterable itr = EdgeIterable.EMPTY; + Assert.assertFalse(itr.spliterator().tryAdvance((e) -> { + })); + Assert.assertEquals(itr.spliterator().estimateSize(), 0); + Assert.assertEquals(itr.stream().collect(Collectors.toList()), Collections.EMPTY_LIST); + } + @Test public void testNodeIterableHasNext() { Iterator itr = NodeIterable.EMPTY.iterator(); @@ -95,6 +106,15 @@ public void testNodeIterableDoBreak() { NodeIterable.EMPTY.doBreak(); } + @Test + public void testNodeIterableSpliterator() { + NodeIterable itr = NodeIterable.EMPTY; + Assert.assertFalse(itr.spliterator().tryAdvance((e) -> { + })); + Assert.assertEquals(itr.spliterator().estimateSize(), 0); + Assert.assertEquals(itr.stream().collect(Collectors.toList()), Collections.EMPTY_LIST); + } + @Test public void testElementIterableHasNext() { Iterator itr = ElementIterable.EMPTY.iterator(); @@ -127,4 +147,13 @@ public void testElementIterableToCollection() { public void testElementIterableDoBreak() { ElementIterable.EMPTY.doBreak(); } + + @Test + public void testElementIterableSpliterator() { + ElementIterable itr = ElementIterable.EMPTY; + Assert.assertFalse(itr.spliterator().tryAdvance((e) -> { + })); + Assert.assertEquals(itr.spliterator().estimateSize(), 0); + Assert.assertEquals(itr.stream().collect(Collectors.toList()), Collections.EMPTY_LIST); + } } diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index 9d4c70ab..d3ee961a 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -22,11 +22,10 @@ import java.awt.Color; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; +import java.util.Spliterator; import org.gephi.graph.api.Column; import org.gephi.graph.api.ColumnIterable; import org.gephi.graph.api.Configuration; @@ -733,8 +732,8 @@ public void testGetNodeEdges() { graphStore.addAllEdges(Arrays.asList(edges)); for (EdgeImpl e : edges) { - testEdgeIterable(graphStore.getEdges(e.source, e.target), new EdgeImpl[] { e }); - testEdgeIterable(graphStore.getEdges(e.source, e.target, e.type), new EdgeImpl[] { e }); + testEdgeIterableWithoutParallel(graphStore.getEdges(e.source, e.target), new EdgeImpl[] { e }); + testEdgeIterableWithoutParallel(graphStore.getEdges(e.source, e.target, e.type), new EdgeImpl[] { e }); } } @@ -746,8 +745,8 @@ public void testGetNodeEdgesUnusedEdgeType() { graphStore.addAllEdges(Arrays.asList(edges)); for (EdgeImpl e : edges) { - testEdgeIterable(graphStore.getEdges(e.source, e.target), new EdgeImpl[] {}); - testEdgeIterable(graphStore.getEdges(e.source, e.target, e.type), new EdgeImpl[] { e }); + testEdgeIterableWithoutParallel(graphStore.getEdges(e.source, e.target), new EdgeImpl[] {}); + testEdgeIterableWithoutParallel(graphStore.getEdges(e.source, e.target, e.type), new EdgeImpl[] { e }); } } @@ -756,8 +755,8 @@ public void testGetNodeEdgesMixed() { GraphStore graphStore = GraphGenerator.generateSmallMixedGraphStore(); for (EdgeImpl e : graphStore.edgeStore.toArray()) { - testEdgeIterable(graphStore.getEdges(e.source, e.target), new EdgeImpl[] { e }); - testEdgeIterable(graphStore.getEdges(e.source, e.target, e.type), new EdgeImpl[] { e }); + testEdgeIterableWithoutParallel(graphStore.getEdges(e.source, e.target), new EdgeImpl[] { e }); + testEdgeIterableWithoutParallel(graphStore.getEdges(e.source, e.target, e.type), new EdgeImpl[] { e }); } } @@ -766,8 +765,8 @@ public void testGetNodeEdgesMixedUnusedEdgeType() { GraphStore graphStore = GraphGenerator.generateSmallMixedGraphStore(2); for (EdgeImpl e : graphStore.edgeStore.toArray()) { - testEdgeIterable(graphStore.getEdges(e.source, e.target), new EdgeImpl[] {}); - testEdgeIterable(graphStore.getEdges(e.source, e.target, e.type), new EdgeImpl[] { e }); + testEdgeIterableWithoutParallel(graphStore.getEdges(e.source, e.target), new EdgeImpl[] {}); + testEdgeIterableWithoutParallel(graphStore.getEdges(e.source, e.target, e.type), new EdgeImpl[] { e }); } } @@ -1057,19 +1056,19 @@ public void testVersion() { // UTILITY private void testNodeIterable(NodeIterable iterable, NodeImpl[] nodes) { - Set nodeSet = new HashSet<>(iterable.toCollection()); - for (NodeImpl n : nodes) { - Assert.assertTrue(nodeSet.remove(n)); - } - Assert.assertEquals(nodeSet.size(), 0); + Assert.assertEquals(iterable.toArray(), nodes); + Assert.assertEquals(iterable.stream().toArray(Node[]::new), nodes); + Assert.assertEquals(iterable.parallelStream().toArray(Node[]::new), nodes); } private void testEdgeIterable(EdgeIterable iterable, EdgeImpl[] edges) { - Set edgeSet = new HashSet<>(iterable.toCollection()); - for (EdgeImpl n : edges) { - Assert.assertTrue(edgeSet.remove(n)); - } - Assert.assertEquals(edgeSet.size(), 0); + testEdgeIterableWithoutParallel(iterable, edges); + Assert.assertEquals(iterable.parallelStream().toArray(Edge[]::new), edges); + } + + private void testEdgeIterableWithoutParallel(EdgeIterable iterable, EdgeImpl[] edges) { + Assert.assertEquals(iterable.toArray(), edges); + Assert.assertEquals(iterable.stream().toArray(Edge[]::new), edges); } private void testBasicStoreEquals(GraphStore graphStore, BasicGraphStore basicGraphStore) { @@ -1193,4 +1192,36 @@ private void testNodeSets(NodeIterable n1, NodeIterable n2) { } Assert.assertEquals(s2.size(), 0); } + + @Test + public void testGetEdgesTypeSpliteratorMatches() { + GraphStore gs = GraphGenerator.generateSmallMultiTypeGraphStore(); + Edge[] edges = gs.getEdges().toArray(); + for (int i = 0; i < 3; i++) { + Spliterator sp = gs.getEdges(i).spliterator(); + int finalI = i; + sp.forEachRemaining(e -> Assert.assertEquals(finalI, e.getType())); + } + } + + // @Test + // public void testGetSelfLoopsSpliteratorMatches() { + // GraphStore gs = new GraphStore(); + // NodeImpl[] nodes = GraphGenerator.generateSmallNodeList(); + // gs.addAllNodes(Arrays.asList(nodes)); + // EdgeImpl[] edges = new EdgeImpl[] { + // GraphGenerator.generateSelfLoop(0, true), + // GraphGenerator.generateSelfLoop(1, false) + // }; + // for (EdgeImpl e : edges) { + // e.source = nodes[0]; + // e.target = nodes[0]; + // gs.addEdge(e); + // } + // Set ids = new HashSet<>(); + // gs.getSelfLoops().spliterator().forEachRemaining(e -> ids.add(e.getId())); + // for (EdgeImpl e : edges) { + // Assert.assertTrue(ids.contains(e.getId())); + // } + // } } diff --git a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java index ff60e577..ad340afb 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java @@ -15,20 +15,18 @@ */ package org.gephi.graph.impl; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectSet; import java.util.Arrays; import java.util.Collections; import java.util.Random; +import java.util.stream.Collectors; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; -import org.gephi.graph.api.Element; -import org.gephi.graph.api.ElementIterable; +import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; +import org.gephi.graph.api.NodeIterable; import org.gephi.graph.api.Rect2D; -import org.gephi.graph.api.SpatialIndex; import org.gephi.graph.api.UndirectedSubgraph; import org.testng.Assert; import org.testng.annotations.Test; @@ -500,26 +498,26 @@ public void testDirectedIterators() { DirectedSubgraph graph = store.getDirectedGraph(view); GraphStore copyGraphStore = convertToStore(view); - Assert.assertTrue(isIterablesEqual(graph.getNodes(), copyGraphStore.getNodes())); - Assert.assertTrue(isIterablesEqual(graph.getEdges(), copyGraphStore.getEdges())); - Assert.assertTrue(isIterablesEqual(graph.getSelfLoops(), copyGraphStore.getSelfLoops())); + assertIterablesEqual(graph.getNodes(), copyGraphStore.getNodes()); + assertIterablesEqual(graph.getEdges(), copyGraphStore.getEdges()); + assertIterablesEqual(graph.getSelfLoops(), copyGraphStore.getSelfLoops()); for (Node n : graph.getNodes()) { Node m = copyGraphStore.getNode(n.getId()); - Assert.assertTrue(isIterablesEqual(graph.getEdges(n), copyGraphStore.getEdges(m))); - Assert.assertTrue(isIterablesEqual(graph.getInEdges(n), copyGraphStore.getInEdges(m))); - Assert.assertTrue(isIterablesEqual(graph.getOutEdges(n), copyGraphStore.getOutEdges(m))); - Assert.assertTrue(isIterablesEqual(graph.getNeighbors(n), copyGraphStore.getNeighbors(m))); - Assert.assertTrue(isIterablesEqual(graph.getSuccessors(n), copyGraphStore.getSuccessors(m))); - Assert.assertTrue(isIterablesEqual(graph.getPredecessors(n), copyGraphStore.getPredecessors(m))); + assertIterablesEqual(graph.getEdges(n), copyGraphStore.getEdges(m)); + assertIterablesEqual(graph.getInEdges(n), copyGraphStore.getInEdges(m)); + assertIterablesEqual(graph.getOutEdges(n), copyGraphStore.getOutEdges(m)); + assertIterablesEqual(graph.getNeighbors(n), copyGraphStore.getNeighbors(m)); + assertIterablesEqual(graph.getSuccessors(n), copyGraphStore.getSuccessors(m)); + assertIterablesEqual(graph.getPredecessors(n), copyGraphStore.getPredecessors(m)); for (int i = 0; i < typeCount; i++) { - Assert.assertTrue(isIterablesEqual(graph.getEdges(n, i), copyGraphStore.getEdges(m, i))); - Assert.assertTrue(isIterablesEqual(graph.getInEdges(n, i), copyGraphStore.getInEdges(m, i))); - Assert.assertTrue(isIterablesEqual(graph.getOutEdges(n, i), copyGraphStore.getOutEdges(m, i))); - Assert.assertTrue(isIterablesEqual(graph.getNeighbors(n, i), copyGraphStore.getNeighbors(m, i))); - Assert.assertTrue(isIterablesEqual(graph.getSuccessors(n, i), copyGraphStore.getSuccessors(m, i))); - Assert.assertTrue(isIterablesEqual(graph.getPredecessors(n, i), copyGraphStore.getPredecessors(m, i))); + assertIterablesEqual(graph.getEdges(n, i), copyGraphStore.getEdges(m, i)); + assertIterablesEqual(graph.getInEdges(n, i), copyGraphStore.getInEdges(m, i)); + assertIterablesEqual(graph.getOutEdges(n, i), copyGraphStore.getOutEdges(m, i)); + assertIterablesEqual(graph.getNeighbors(n, i), copyGraphStore.getNeighbors(m, i)); + assertIterablesEqual(graph.getSuccessors(n, i), copyGraphStore.getSuccessors(m, i)); + assertIterablesEqual(graph.getPredecessors(n, i), copyGraphStore.getPredecessors(m, i)); } } } @@ -535,21 +533,18 @@ public void testUndirectedIterators() { UndirectedSubgraph graph = store.getUndirectedGraph(view); GraphStore copyGraphStore = convertToStore(view); - Assert.assertTrue(isIterablesEqual(graph.getNodes(), copyGraphStore.undirectedDecorator.getNodes())); - Assert.assertTrue(isIterablesEqual(graph.getEdges(), copyGraphStore.undirectedDecorator.getEdges())); - Assert.assertTrue(isIterablesEqual(graph.getSelfLoops(), copyGraphStore.undirectedDecorator.getSelfLoops())); + assertIterablesEqual(graph.getNodes(), copyGraphStore.undirectedDecorator.getNodes()); + assertIterablesEqual(graph.getEdges(), copyGraphStore.undirectedDecorator.getEdges()); + assertIterablesEqual(graph.getSelfLoops(), copyGraphStore.undirectedDecorator.getSelfLoops()); for (Node n : graph.getNodes()) { Node m = copyGraphStore.getNode(n.getId()); - Assert.assertTrue(isIterablesEqual(graph.getEdges(n), copyGraphStore.undirectedDecorator.getEdges(m))); - Assert.assertTrue(isIterablesEqual(graph.getNeighbors(n), copyGraphStore.undirectedDecorator - .getNeighbors(m))); + assertIterablesEqual(graph.getEdges(n), copyGraphStore.undirectedDecorator.getEdges(m)); + assertIterablesEqual(graph.getNeighbors(n), copyGraphStore.undirectedDecorator.getNeighbors(m)); for (int i = 0; i < typeCount; i++) { - Assert.assertTrue(isIterablesEqual(graph.getEdges(n, i), copyGraphStore.undirectedDecorator - .getEdges(m, i))); - Assert.assertTrue(isIterablesEqual(graph.getNeighbors(n, i), copyGraphStore.undirectedDecorator - .getNeighbors(m, i))); + assertIterablesEqual(graph.getEdges(n, i), copyGraphStore.undirectedDecorator.getEdges(m, i)); + assertIterablesEqual(graph.getNeighbors(n, i), copyGraphStore.undirectedDecorator.getNeighbors(m, i)); } } } @@ -892,35 +887,6 @@ public void testIntersection() { Assert.assertTrue(graph1.contains(n2)); } - // UTILITY - private boolean isIterablesEqual(ElementIterable n1, ElementIterable n2) { - ObjectSet s1 = new ObjectOpenHashSet(); - for (Object n : n1) { - s1.add(((Element) n).getId()); - } - ObjectSet s2 = new ObjectOpenHashSet(); - for (Object n : n2) { - s2.add(((Element) n).getId()); - } - return s1.equals(s2); - } - - private GraphStore convertToStore(GraphViewImpl view) { - GraphStore store = new GraphStore(); - DirectedSubgraph graph = view.getDirectedGraph(); - for (Node n : graph.getNodes()) { - NodeImpl m = new NodeImpl(n.getId()); - store.addNode(m); - } - for (Edge e : graph.getEdges()) { - NodeImpl source = store.getNode(e.getSource().getId()); - NodeImpl target = store.getNode(e.getTarget().getId()); - EdgeImpl f = new EdgeImpl(e.getId(), source, target, e.getType(), e.getWeight(), e.isDirected()); - store.addEdge(f); - } - return store; - } - @Test public void testGetBoundariesEmptyView() { GraphStore graphStore = GraphGenerator.generateEmptyGraphStore(getSpatialConfig()); @@ -1104,6 +1070,35 @@ public void testGetBoundariesAfterViewChanges() { Assert.assertEquals(expected, graph.getSpatialIndex().getBoundaries()); } + // UTILITY + private void assertIterablesEqual(NodeIterable n1, NodeIterable n2) { + Assert.assertEquals(n1.toCollection(), n2.toCollection()); + Assert.assertEquals(n1.stream().collect(Collectors.toList()), n2.stream().collect(Collectors.toList())); + Assert.assertEquals(n1.toArray(), n2.toArray()); + } + + private void assertIterablesEqual(EdgeIterable e1, EdgeIterable e2) { + Assert.assertEquals(e1.toCollection(), e2.toCollection()); + Assert.assertEquals(e1.stream().collect(Collectors.toList()), e2.stream().collect(Collectors.toList())); + Assert.assertEquals(e1.toArray(), e2.toArray()); + } + + private GraphStore convertToStore(GraphViewImpl view) { + GraphStore store = new GraphStore(); + DirectedSubgraph graph = view.getDirectedGraph(); + for (Node n : graph.getNodes()) { + NodeImpl m = new NodeImpl(n.getId()); + store.addNode(m); + } + for (Edge e : graph.getEdges()) { + NodeImpl source = store.getNode(e.getSource().getId()); + NodeImpl target = store.getNode(e.getTarget().getId()); + EdgeImpl f = new EdgeImpl(e.getId(), source, target, e.getType(), e.getWeight(), e.isDirected()); + store.addEdge(f); + } + return store; + } + private void addSomeElements(GraphStore store, GraphViewImpl view) { double perc = 0.8; Random rand = new Random(98324); diff --git a/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java index eb6561a1..2efe9dba 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java @@ -15,11 +15,11 @@ */ package org.gephi.graph.impl; -import org.gephi.graph.api.Interval; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; import org.gephi.graph.api.Node; import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedSubgraph; diff --git a/src/test/java/org/gephi/graph/impl/NodeStoreTest.java b/src/test/java/org/gephi/graph/impl/NodeStoreTest.java index 4b1c6a1a..2c320bfd 100644 --- a/src/test/java/org/gephi/graph/impl/NodeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/NodeStoreTest.java @@ -20,10 +20,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.ConcurrentModificationException; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; +import java.util.Spliterator; +import java.util.stream.Collectors; import org.gephi.graph.api.Node; import org.testng.Assert; import org.testng.annotations.Test; @@ -502,6 +505,167 @@ public void testDictionaryDuplicate() { nodeStore.add(node2); } + @Test + public void testSpliteratorCoversAll() { + NodeStore nodeStore = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateLargeNodeList()); + nodeStore.addAll(nodes); + + List seen = new ArrayList<>(); + Spliterator sp = nodeStore.spliterator(); + Assert.assertEquals(sp.estimateSize(), nodes.size()); + sp.forEachRemaining(e -> seen.add((NodeImpl) e)); + + Assert.assertEquals(seen, nodes); + } + + @Test + public void testSpliteratorSizeReduce() { + NodeStore nodeStore = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateSmallNodeList()); + nodeStore.addAll(nodes); + + Spliterator sp = nodeStore.spliterator(); + long size = sp.estimateSize(); + sp.tryAdvance(n -> { + }); + Assert.assertEquals(sp.estimateSize(), size - 1); + } + + @Test + public void testSpliteratorSizeReduceWithGarbage() { + NodeStore nodeStore = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateSmallNodeList()); + nodeStore.addAll(nodes); + nodeStore.remove(nodes.get(0)); + + Spliterator sp = nodeStore.spliterator(); + Assert.assertEquals(sp.estimateSize(), nodes.size() - 1); + + // Last node + nodeStore.remove(nodes.get(nodes.size() - 1)); + sp = nodeStore.spliterator(); + Assert.assertEquals(sp.estimateSize(), nodes.size() - 2); + } + + @Test + public void testSpliteratorParallel() { + NodeStore nodeStore = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateLargeNodeList()); + nodeStore.addAll(nodes); + + List seen = nodeStore.parallelStream().collect(Collectors.toList()); + + Assert.assertEquals(seen, nodes); + } + + @Test + public void testSpliteratorEmpty() { + NodeStore nodeStore = new NodeStore(); + + Assert.assertEquals(nodeStore.parallelStream().count(), 0); + Assert.assertEquals(nodeStore.spliterator().estimateSize(), 0); + } + + @Test + public void testSpliteratorEmptyAfterRemove() { + NodeStore nodeStore = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateLargeNodeList()); + nodeStore.addAll(nodes); + nodeStore.removeAll(nodes); + + Assert.assertEquals(nodeStore.parallelStream().count(), 0); + Assert.assertEquals(nodeStore.spliterator().estimateSize(), 0); + } + + @Test + public void testSpliteratorParallelLarge() { + NodeStore nodeStore = new NodeStore(); + List nodes = Arrays + .asList(GraphGenerator.generateNodeList(GraphStoreConfiguration.NODESTORE_BLOCK_SIZE * 4 + 10)); + nodeStore.addAll(nodes); + + List seen = nodeStore.parallelStream().collect(Collectors.toList()); + + Assert.assertEquals(seen, nodes); + } + + @Test + public void testSpliteratorParallelAfterRemove() { + NodeStore nodeStore = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateSmallNodeList()); + nodeStore.addAll(nodes); + nodeStore.remove(nodes.get(0)); + + List seen = nodeStore.parallelStream().collect(Collectors.toList()); + + Assert.assertEquals(seen, nodes.subList(1, nodes.size())); + } + + @Test(expectedExceptions = ConcurrentModificationException.class) + public void testSpliteratorFailFastOnAdd() { + NodeStore store = new NodeStore(null, null, null, null, new GraphVersion(null)); + store.add(new NodeImpl("a")); + Spliterator sp = store.spliterator(); + store.add(new NodeImpl("b")); + sp.tryAdvance(x -> { + }); + } + + @Test + public void testParallelStreamCount() { + NodeStore store = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateLargeNodeList()); + store.addAll(nodes); + + long count = store.parallelStream().count(); + Assert.assertEquals(count, store.size()); + } + + @Test + public void testParallelStreamForEach() { + NodeStore store = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateLargeNodeList()); + store.addAll(nodes); + + Set set = Collections.synchronizedSet(new HashSet<>()); + store.parallelStream().forEach(set::add); + Assert.assertEquals(set, store.toSet()); + } + + @Test + public void testParallelStreamForEachOrdered() { + NodeStore store = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateLargeNodeList()); + store.addAll(nodes); + + Set set = Collections.synchronizedSet(new HashSet<>()); + store.parallelStream().forEachOrdered(set::add); + Assert.assertEquals(set, store.toSet()); + } + + @Test + public void testParallelStreamForEachOrderedAfterRemove() { + NodeStore store = new NodeStore(); + List nodes = Arrays.asList(GraphGenerator.generateLargeNodeList()); + store.addAll(nodes); + + Random random = new Random(); + for (int i = 0; i < nodes.size() / 5; i++) { + int r = random.nextInt(store.maxStoreId()); + Node n = store.getForGetByStoreId(r); + if (n != null) { + store.remove(n); + } + } + + Set set = Collections.synchronizedSet(new HashSet<>()); + store.parallelStream().forEachOrdered(set::add); + Assert.assertEquals(set, store.toSet()); + } + + // Utils + private void testContainsOnly(NodeStore store, List list) { for (NodeImpl n : list) { Assert.assertTrue(store.contains(n)); diff --git a/src/test/java/org/gephi/graph/impl/UndirectedDecoratorTest.java b/src/test/java/org/gephi/graph/impl/UndirectedDecoratorTest.java index 0ca95d96..41d47d29 100644 --- a/src/test/java/org/gephi/graph/impl/UndirectedDecoratorTest.java +++ b/src/test/java/org/gephi/graph/impl/UndirectedDecoratorTest.java @@ -15,12 +15,10 @@ */ package org.gephi.graph.impl; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; -import java.util.Iterator; -import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Node; @@ -205,7 +203,11 @@ public void testGetEdgesMixed() { // UTILITY private void testEdgeIterable(EdgeIterable iterable, Edge[] edges) { - Set edgeSet = new HashSet<>(iterable.toCollection()); + testEdgeIterable(new HashSet<>(iterable.toCollection()), edges); + testEdgeIterable(iterable.stream().collect(Collectors.toSet()), edges); + } + + private void testEdgeIterable(Set edgeSet, Edge[] edges) { for (Edge n : edges) { Assert.assertTrue(edgeSet.remove(n)); } From 57f0d102514879b2d285b4e4a2b7282a0714e8fc Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 3 Oct 2025 15:24:12 +0200 Subject: [PATCH 182/271] Add Spliterator on NodesQuadTree (#243) * Change default config for block size and default dict size * Replace LinkedHashSet with arrays and implement spliterator * Add multi-node edge iterator * Refactoring to remove duplicate getNodes methods in quadtree * Add edge iteration support in quadtree * Add global quad tree edge iterator option * Refactor to configure edge inout iterator locking and test all edges in quadtree spliterator * Formatting * Git ignore * Non approximate support for global iterator and bugfix * Add additional tests * Quadtree versioning * Tweak boundaries * Documentation * Update src/main/java/org/gephi/graph/impl/NodesQuadTree.java Remove distinct from spliterator as edges can be returned twice Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix locking function name clash in view decorator * Fix toArray * Reduce memory overhead of quad node array init --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .gitignore | 1 + src/main/java/org/gephi/graph/api/Rect2D.java | 42 +- .../org/gephi/graph/api/SpatialIndex.java | 54 +- .../java/org/gephi/graph/impl/EdgeStore.java | 109 +- .../java/org/gephi/graph/impl/GraphStore.java | 10 +- .../graph/impl/GraphStoreConfiguration.java | 10 +- .../gephi/graph/impl/GraphViewDecorator.java | 98 +- .../org/gephi/graph/impl/GraphViewImpl.java | 14 +- .../java/org/gephi/graph/impl/NodeStore.java | 2 +- .../org/gephi/graph/impl/NodesQuadTree.java | 1163 ++++++++++++++++- .../gephi/graph/impl/SpatialIndexImpl.java | 115 +- .../gephi/graph/impl/SpatialNodeDataImpl.java | 14 + .../gephi/graph/impl/UndirectedDecorator.java | 2 +- .../org/gephi/graph/impl/EdgeStoreTest.java | 90 +- .../org/gephi/graph/impl/GraphGenerator.java | 12 + .../gephi/graph/impl/NodesQuadTreeTest.java | 353 ++++- .../graph/impl/SpatialIndexImplTest.java | 30 +- 17 files changed, 1884 insertions(+), 235 deletions(-) diff --git a/.gitignore b/.gitignore index a4ccad2a..e56582dc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ nbactions.xml /store/graphstore/target/ .idea *.iml +.vscode/** \ No newline at end of file diff --git a/src/main/java/org/gephi/graph/api/Rect2D.java b/src/main/java/org/gephi/graph/api/Rect2D.java index 98ab3876..3970a908 100644 --- a/src/main/java/org/gephi/graph/api/Rect2D.java +++ b/src/main/java/org/gephi/graph/api/Rect2D.java @@ -114,8 +114,8 @@ public String toString() { } private String toString(NumberFormat formatter) { - return "(" + formatter.format(minX) + " " + formatter.format(minY) + ") < " + "(" + formatter - .format(maxX) + " " + formatter.format(maxY) + ")"; + return "min(x:" + formatter.format(minX) + " y:" + formatter.format(minY) + ") < " + "max(x:" + formatter + .format(maxX) + " y:" + formatter.format(maxY) + ")"; } /** @@ -174,6 +174,44 @@ public boolean intersects(float minX, float minY, float maxX, float maxY) { return this.minX <= maxX && minX <= this.maxX && this.maxY >= minY && maxY >= this.minY; } + /** + * Returns true if this rectangle contains or intersects with the given + * rectangle. This is equivalent to checking + * {@code this.contains(rect) || this.intersects(rect)} but more efficient as it + * performs the check in a single operation. + * + * @param rect the rectangle to check + * @return true if this rectangle contains or intersects with the given + * rectangle, false otherwise + */ + public boolean containsOrIntersects(Rect2D rect) { + if (rect == this) { + return true; + } + + return containsOrIntersects(rect.minX, rect.minY, rect.maxX, rect.maxY); + } + + /** + * Returns true if this rectangle contains or intersects with the given + * rectangle. This is equivalent to checking + * {@code this.contains(minX, minY, maxX, maxY) || this.intersects(minX, minY, maxX, maxY)} + * but more efficient as it performs the check in a single operation. + * + * @param minX the x coordinate of the minimum corner + * @param minY the y coordinate of the minimum corner + * @param maxX the x coordinate of the maximum corner + * @param maxY the y coordinate of the maximum corner + * + * @return true if this rectangle contains or intersects with the given + * rectangle, false otherwise + */ + public boolean containsOrIntersects(float minX, float minY, float maxX, float maxY) { + // Two rectangles have overlap if they intersect - containment is a subset of + // intersection + return this.minX <= maxX && minX <= this.maxX && this.maxY >= minY && maxY >= this.minY; + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java index 997c9040..3502b669 100644 --- a/src/main/java/org/gephi/graph/api/SpatialIndex.java +++ b/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -16,7 +16,19 @@ package org.gephi.graph.api; /** - * Object to query the nodes and edges of the graph in a spatial context. + * Query the (quadtree-based) index based on the given rectangle area. + *

+ * The spatial index is not enabled by default. To enable it, set the + * appropriate configuration: + * @{@link Configuration.Builder#enableSpatialIndex(boolean)}. + *

+ * When nodes are moved, added or removed, the spatial index is automatically + * updated. Edges are not indexed, but they are queried based on whether their + * source or target nodes are in the given area. + *

+ * The Z position is not taken into account when querying the spatial index, + * only X/Y are supported. + *

* * @author Eduardo Ramos */ @@ -31,13 +43,35 @@ public interface SpatialIndex { NodeIterable getNodesInArea(Rect2D rect); /** - * Returns the edges in the given area. + * Returns the nodes in the given area using a faster, but approximate method. + *

+ * All nodes in the provided area are guaranteed to be returned, but some nodes + * outside the area may also be returned. + * + * @param rect area to query + * @return nodes in the area + */ + NodeIterable getApproximateNodesInArea(Rect2D rect); + + /** + * Returns the edges in the given area. Edges may be returned twice. * * @param rect area to query * @return edges in the area */ EdgeIterable getEdgesInArea(Rect2D rect); + /** + * Returns the edges in the given area using a faster, but approximate method. + *

+ * All edges in the provided area are guaranteed to be returned, but some edges + * outside the area may also be returned. Edges may also be returned twice. + * + * @param rect area to query + * @return edges in the area + */ + EdgeIterable getApproximateEdgesInArea(Rect2D rect); + /** * Returns the bounding rectangle that contains all nodes in the graph. The * boundaries are calculated based on each node's position and size. @@ -45,4 +79,20 @@ public interface SpatialIndex { * @return the bounding rectangle, or null if there are no nodes */ Rect2D getBoundaries(); + + /** + * Acquires a read lock on the spatial index. This is recommended when using the + * query functions in a stream context, to avoid the spatial index being + * modified while being queried. + *

+ * Every call to this method must be matched with a call to + * {@link #spatialIndexReadUnlock()}. + */ + void spatialIndexReadLock(); + + /** + * Releases a read lock on the spatial index. This must be called after a call + * to {@link #spatialIndexReadLock()}. + */ + void spatialIndexReadUnlock(); } diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 497c7a00..b03277ba 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -419,14 +419,18 @@ public EdgeInIterator edgeInIterator(final Node node) { return new EdgeInIterator((NodeImpl) node); } - public EdgeInOutIterator edgeIterator(final Node node) { + public EdgeInOutIterator edgeIterator(final Node node, boolean locking) { checkValidNodeObject(node); - return new EdgeInOutIterator((NodeImpl) node); + return new EdgeInOutIterator((NodeImpl) node, locking); } - public Iterator edgeUndirectedIterator(final Node node) { + public EdgeInOutMultiIterator edgeIterator(final Iterator nodeIterator, boolean locking) { + return new EdgeInOutMultiIterator(nodeIterator, locking); + } + + public Iterator edgeUndirectedIterator(final Node node, boolean locking) { checkValidNodeObject(node); - return undirectedIterator(new EdgeInOutIterator((NodeImpl) node)); + return undirectedIterator(new EdgeInOutIterator((NodeImpl) node, locking)); } public EdgeTypeOutIterator edgeOutIterator(final Node node, int type) { @@ -471,7 +475,7 @@ public NeighborsIterator neighborInIterator(final Node node, int type) { public NeighborsIterator neighborIterator(Node node) { checkValidNodeObject(node); - return new NeighborsUndirectedIterator((NodeImpl) node, new EdgeInOutIterator((NodeImpl) node)); + return new NeighborsUndirectedIterator((NodeImpl) node, new EdgeInOutIterator((NodeImpl) node, true)); } public NeighborsIterator neighborIterator(final Node node, int type) { @@ -1285,6 +1289,9 @@ private void incrementVersion() { if (version != null) { version.incrementAndGetEdgeVersion(); } + if (spatialIndex != null) { + spatialIndex.incrementVersion(); + } } boolean isUndirectedToIgnore(EdgeImpl edge) { @@ -1491,10 +1498,15 @@ public boolean hasNext() { } } - protected final class EdgeInOutIterator implements Iterator { + /** + * Abstract base class for iterating over edges connected to nodes. Provides + * common logic for handling both incoming and outgoing edges. + */ + protected abstract class AbstractEdgeInOutIterator implements Iterator { - protected final int outTypeLength; - protected final int inTypeLength; + protected final boolean locking; + protected int outTypeLength; + protected int inTypeLength; protected EdgeImpl[] outArray; protected EdgeImpl[] inArray; protected int typeIndex = 0; @@ -1502,17 +1514,36 @@ protected final class EdgeInOutIterator implements Iterator { protected EdgeImpl lastEdge; protected boolean out = true; - public EdgeInOutIterator(NodeImpl node) { - readLock(); + protected AbstractEdgeInOutIterator(boolean locking) { + this.locking = locking; + if (locking) { + readLock(); + } + } + + /** + * Initialize arrays for the current node. Called when starting iteration for a + * new node. + */ + protected void initializeForNode(NodeImpl node) { outArray = node.headOut; outTypeLength = outArray.length; inArray = node.headIn; inTypeLength = inArray.length; + typeIndex = 0; + pointer = null; + out = true; } + /** + * Called when the current node has no more edges. Should return true if there + * are more nodes to process, false otherwise. + */ + protected abstract boolean moveToNextNode(); + @Override public boolean hasNext() { - if (pointer == null) { + while (pointer == null) { if (out) { while (pointer == null && typeIndex < outTypeLength) { pointer = outArray[typeIndex++]; @@ -1537,8 +1568,13 @@ public boolean hasNext() { } if (pointer == null) { - readUnlock(); - return false; + // No more edges for current node, try next node + if (!moveToNextNode()) { + if (locking) { + readUnlock(); + } + return false; + } } } return true; @@ -1579,6 +1615,53 @@ public void remove() { } } + /** + * Iterator for edges connected to a single node (both incoming and outgoing). + */ + protected final class EdgeInOutIterator extends AbstractEdgeInOutIterator { + + public EdgeInOutIterator(NodeImpl node, boolean locking) { + super(locking); + initializeForNode(node); + } + + @Override + protected boolean moveToNextNode() { + // Single node iterator - no more nodes to process + return false; + } + } + + /** + * Iterator for edges connected to multiple nodes (both incoming and outgoing). + * Iterates through all edges of all provided nodes without creating separate + * iterators. + */ + protected final class EdgeInOutMultiIterator extends AbstractEdgeInOutIterator { + + private final Iterator nodeIterator; + + public EdgeInOutMultiIterator(Iterator nodeIterator, boolean locking) { + super(locking); + this.nodeIterator = nodeIterator; + // Initialize with first node if available + if (nodeIterator.hasNext()) { + NodeImpl node = nodeIterator.next(); + checkValidNodeObject(node); + initializeForNode(node); + } + } + + @Override + protected boolean moveToNextNode() { + if (nodeIterator.hasNext()) { + initializeForNode(nodeIterator.next()); + return true; + } + return false; + } + } + protected final class EdgeOutIterator implements Iterator { protected final int typeLength; diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 0bb033a1..d93aff11 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -277,7 +277,8 @@ public boolean removeNode(final Node node) { autoWriteLock(); try { nodeStore.checkNonNullNodeObject(node); - for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(node); edgeIterator.hasNext();) { + for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(node, false); edgeIterator + .hasNext();) { edgeIterator.next(); edgeIterator.remove(); } @@ -303,7 +304,8 @@ public boolean removeAllNodes(Collection nodes) { try { for (Node node : nodes) { nodeStore.checkNonNullNodeObject(node); - for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(node); edgeIterator.hasNext();) { + for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(node, false); edgeIterator + .hasNext();) { edgeIterator.next(); edgeIterator.remove(); } @@ -434,7 +436,7 @@ public NodeIterable getSuccessors(final Node node, final int type) { @Override public EdgeIterable getEdges(final Node node) { - return new EdgeIterableWrapper(() -> edgeStore.edgeIterator(node), getAutoLock()); + return new EdgeIterableWrapper(() -> edgeStore.edgeIterator(node, true), getAutoLock()); } @Override @@ -575,7 +577,7 @@ public boolean isIncident(final Node node, final Edge edge) { public void clearEdges(final Node node) { autoWriteLock(); try { - EdgeStore.EdgeInOutIterator itr = edgeStore.edgeIterator(node); + EdgeStore.EdgeInOutIterator itr = edgeStore.edgeIterator(node, false); for (; itr.hasNext();) { itr.next(); itr.remove(); diff --git a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java index 02a4e0f4..3436ad9c 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java +++ b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java @@ -40,10 +40,10 @@ public final class GraphStoreConfiguration { public static final int NODESTORE_DEFAULT_DICTIONARY_SIZE = 8192; public final static float NODESTORE_DICTIONARY_LOAD_FACTOR = .7f; // EdgeStore - public static final int EDGESTORE_BLOCK_SIZE = 8192; - public static final int EDGESTORE_DEFAULT_BLOCKS = 10; + public static final int EDGESTORE_BLOCK_SIZE = 32768; + public static final int EDGESTORE_DEFAULT_BLOCKS = 5; public static final int EDGESTORE_DEFAULT_TYPE_COUNT = 1; - public static final int EDGESTORE_DEFAULT_DICTIONARY_SIZE = 1000; + public static final int EDGESTORE_DEFAULT_DICTIONARY_SIZE = 32768; public static final float EDGESTORE_DICTIONARY_LOAD_FACTOR = .7f; // GraphView public static final int VIEW_DEFAULT_TYPE_COUNT = 1; @@ -83,8 +83,10 @@ public final class GraphStoreConfiguration { public static final TimeRepresentation DEFAULT_TIME_REPRESENTATION = TimeRepresentation.TIMESTAMP; // Spatial index public static final int SPATIAL_INDEX_MAX_LEVELS = 16; - public static final int SPATIAL_INDEX_MAX_OBJECTS_PER_NODE = 5000; + public static final int SPATIAL_INDEX_MAX_OBJECTS_PER_NODE = 8192; public static final float SPATIAL_INDEX_DIMENSION_BOUNDARY = 1e6f; + public static final boolean SPATIAL_INDEX_APPROXIMATE_AREA_SEARCH = false; + public static final float SPATIAL_INDEX_LOCAL_ITERATOR_THRESHOLD = 0.3f; // Miscellaneous public static final double TIMESTAMP_STORE_GROWING_FACTOR = 1.1; public static final double INTERVAL_STORE_GROWING_FACTOR = 1.1; diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 741016c5..1a05db41 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -299,7 +299,7 @@ public boolean contains(Node node) { checkValidNodeObject(node); graphStore.autoReadLock(); try { - return view.containsNode((NodeImpl) node); + return view.containsNode(node); } finally { graphStore.autoReadUnlock(); } @@ -433,7 +433,7 @@ public NodeIterable getNeighbors(Node node) { checkValidInViewNodeObject(node); return new NodeIterableWrapper( () -> new NeighborsIterator((NodeImpl) node, - new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node))), + new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node, true))), graphStore.getAutoLock()); } @@ -451,10 +451,10 @@ public EdgeIterable getEdges(Node node) { checkValidInViewNodeObject(node); if (undirected) { return new EdgeIterableWrapper( - () -> new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node)), + () -> new UndirectedEdgeViewIterator(graphStore.edgeStore.edgeIterator(node, true)), graphStore.getAutoLock()); } else { - return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.edgeIterator(node)), + return new EdgeIterableWrapper(() -> new EdgeViewIterator(graphStore.edgeStore.edgeIterator(node, true)), graphStore.getAutoLock()); } } @@ -507,7 +507,7 @@ public Node getOpposite(Node node, Edge edge) { public int getDegree(Node node) { if (undirected) { int count = 0; - EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node); + EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node, true); while (itr.hasNext()) { EdgeImpl edge = itr.next(); if (view.containsEdge(edge) && !isUndirectedToIgnore(edge)) { @@ -520,7 +520,7 @@ public int getDegree(Node node) { return count; } else { int count = 0; - EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node); + EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node, true); while (itr.hasNext()) { EdgeImpl edge = itr.next(); if (view.containsEdge(edge)) { @@ -598,7 +598,7 @@ public boolean isIncident(final Node node, final Edge edge) { public void clearEdges(Node node) { graphStore.autoWriteLock(); try { - EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node); + EdgeStore.EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node, false); while (itr.hasNext()) { EdgeImpl edge = itr.next(); view.removeEdge(edge); @@ -812,7 +812,7 @@ void checkValidNodeObject(final Node n) { if (!(n instanceof NodeImpl)) { throw new ClassCastException("Object must be a NodeImpl object"); } - if (((NodeImpl) n).storeId == NodeStore.NULL_ID) { + if (n.getStoreId() == NodeStore.NULL_ID) { throw new IllegalArgumentException("Node should belong to a store"); } } @@ -820,7 +820,7 @@ void checkValidNodeObject(final Node n) { void checkValidInViewNodeObject(final Node n) { checkValidNodeObject(n); - if (!view.containsNode((NodeImpl) n)) { + if (!view.containsNode(n)) { throw new RuntimeException("Node doesn't belong to this view"); } } @@ -832,7 +832,7 @@ void checkValidEdgeObject(final Edge n) { if (!(n instanceof EdgeImpl)) { throw new ClassCastException("Object must be a EdgeImpl object"); } - if (((EdgeImpl) n).storeId == EdgeStore.NULL_ID) { + if (n.getStoreId() == EdgeStore.NULL_ID) { throw new IllegalArgumentException("Edge should belong to a store"); } } @@ -840,7 +840,7 @@ void checkValidEdgeObject(final Edge n) { void checkValidInViewEdgeObject(final Edge e) { checkValidEdgeObject(e); - if (!view.containsEdge((EdgeImpl) e)) { + if (!view.containsEdge(e)) { throw new RuntimeException("Edge doesn't belong to this view"); } } @@ -871,9 +871,15 @@ public NodeIterable getNodesInArea(Rect2D rect) { if (graphStore.spatialIndex == null) { throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); } - return new NodeIterableWrapper( - () -> new NodeViewIterator(graphStore.spatialIndex.getNodesInArea(rect).iterator()), - graphStore.spatialIndex.nodesTree.lock); + return graphStore.spatialIndex.getNodesInArea(rect, view::containsNode); + } + + @Override + public NodeIterable getApproximateNodesInArea(Rect2D rect) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return graphStore.spatialIndex.getApproximateNodesInArea(rect, view::containsNode); } @Override @@ -881,49 +887,39 @@ public EdgeIterable getEdgesInArea(Rect2D rect) { if (graphStore.spatialIndex == null) { throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); } - return new EdgeIterableWrapper( - () -> new EdgeViewIterator(graphStore.spatialIndex.getEdgesInArea(rect).iterator()), - graphStore.spatialIndex.nodesTree.lock); + return graphStore.spatialIndex.getEdgesInArea(rect, view::containsEdge); + } + + @Override + public EdgeIterable getApproximateEdgesInArea(Rect2D rect) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return graphStore.spatialIndex.getApproximateEdgesInArea(rect, view::containsEdge); } @Override public Rect2D getBoundaries() { - graphStore.autoReadLock(); - try { - float minX = Float.POSITIVE_INFINITY; - float minY = Float.POSITIVE_INFINITY; - float maxX = Float.NEGATIVE_INFINITY; - float maxY = Float.NEGATIVE_INFINITY; - - boolean hasNodes = false; - - // Iterate only through nodes visible in this view - for (Node node : getNodes()) { - hasNodes = true; - final float x = node.x(); - final float y = node.y(); - final float size = node.size(); - - final float nodeMinX = x - size; - final float nodeMinY = y - size; - final float nodeMaxX = x + size; - final float nodeMaxY = y + size; - - if (nodeMinX < minX) - minX = nodeMinX; - if (nodeMinY < minY) - minY = nodeMinY; - if (nodeMaxX > maxX) - maxX = nodeMaxX; - if (nodeMaxY > maxY) - maxY = nodeMaxY; - } + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return graphStore.spatialIndex.getBoundaries(view::containsNode); + } - return hasNodes ? new Rect2D(minX, minY, maxX, maxY) : new Rect2D(Float.NEGATIVE_INFINITY, - Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); - } finally { - graphStore.autoReadUnlock(); + @Override + public void spatialIndexReadLock() { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + graphStore.spatialIndex.spatialIndexReadLock(); + } + + @Override + public void spatialIndexReadUnlock() { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); } + graphStore.spatialIndex.spatialIndexReadUnlock(); } private final class NodeViewSpliterator implements Spliterator { diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index a797f2a2..6842d67a 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -135,7 +135,7 @@ public boolean addNode(final Node node) { if (nodeView && !edgeView) { // Add edges - EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node); + EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node, false); while (itr.hasNext()) { EdgeImpl edge = itr.next(); NodeImpl opposite = edge.source == nodeImpl ? edge.target : edge.source; @@ -234,7 +234,7 @@ public boolean removeNode(final Node node) { } // Remove edges - EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node); + EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node, false); while (itr.hasNext()) { EdgeImpl edgeImpl = itr.next(); @@ -462,15 +462,15 @@ public void fill() { } } - public boolean containsNode(final NodeImpl node) { + public boolean containsNode(final Node node) { if (!nodeView) { return true; } - return nodeBitVector.get(node.storeId); + return nodeBitVector.get(node.getStoreId()); } - public boolean containsEdge(final EdgeImpl edge) { - return edgeBitVector.get(edge.storeId); + public boolean containsEdge(final Edge edge) { + return edgeBitVector.get(edge.getStoreId()); } public void intersection(final GraphViewImpl otherView) { @@ -915,7 +915,7 @@ private void checkValidNodeObject(final Node n) { if (!(n instanceof NodeImpl)) { throw new ClassCastException("Object must be a NodeImpl object"); } - if (((NodeImpl) n).storeId == NodeStore.NULL_ID) { + if (n.getStoreId() == NodeStore.NULL_ID) { throw new IllegalArgumentException("Node should belong to a store"); } } diff --git a/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java index 0ec778c9..14e43744 100644 --- a/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -687,7 +687,7 @@ public NodeImpl next() { public void remove() { checkWriteLock(); if (edgeStore != null) { - for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(pointer); edgeIterator + for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(pointer, false); edgeIterator .hasNext();) { edgeIterator.next(); edgeIterator.remove(); diff --git a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index 172c3490..a0996d64 100644 --- a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -1,14 +1,35 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Deque; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.ConcurrentModificationException; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; import org.gephi.graph.api.Rect2D; @@ -25,16 +46,23 @@ public class NodesQuadTree { private final QuadTreeNode quadTreeRoot; private final int maxLevels; private final int maxObjectsPerNode; + private final GraphStore graphStore; + private int version = 0; public NodesQuadTree(Rect2D rect) { - this(rect, GraphStoreConfiguration.SPATIAL_INDEX_MAX_LEVELS, + this(null, rect); + } + + public NodesQuadTree(GraphStore store, Rect2D rect) { + this(store, rect, GraphStoreConfiguration.SPATIAL_INDEX_MAX_LEVELS, GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE); } - public NodesQuadTree(Rect2D rect, int maxLevels, int maxObjectsPerNode) { + public NodesQuadTree(GraphStore store, Rect2D rect, int maxLevels, int maxObjectsPerNode) { this.quadTreeRoot = new QuadTreeNode(rect); this.maxLevels = maxLevels; this.maxObjectsPerNode = maxObjectsPerNode; + this.graphStore = store; } public Rect2D quadRect() { @@ -45,14 +73,42 @@ public NodeIterable getNodes(Rect2D searchRect) { return quadTreeRoot.getNodes(searchRect); } - public NodeIterable getNodes(float minX, float minY, float maxX, float maxY) { - return quadTreeRoot.getNodes(new Rect2D(minX, minY, maxX, maxY)); + public NodeIterable getNodes(Rect2D searchRect, boolean approximate) { + return quadTreeRoot.getNodes(searchRect, approximate); + } + + public NodeIterable getNodes(Rect2D searchRect, boolean approximate, Predicate predicate) { + return quadTreeRoot.getNodes(searchRect, approximate, predicate); } public NodeIterable getAllNodes() { return quadTreeRoot.getAllNodes(); } + public NodeIterable getAllNodes(Predicate predicate) { + return quadTreeRoot.getAllNodes(predicate); + } + + public EdgeIterable getEdges() { + return quadTreeRoot.getAllEdges(); + } + + public EdgeIterable getEdges(Rect2D searchRect) { + return quadTreeRoot.getEdges(searchRect); + } + + public EdgeIterable getEdges(Rect2D searchRect, boolean approximate) { + return quadTreeRoot.getEdges(searchRect, approximate); + } + + public EdgeIterable getEdges(Rect2D searchRect, boolean approximate, Predicate predicate) { + return quadTreeRoot.getEdges(searchRect, approximate, predicate); + } + + public void incrementVersion() { + version++; + } + public boolean updateNode(NodeImpl item, float minX, float minY, float maxX, float maxY) { writeLock(); try { @@ -60,6 +116,7 @@ public boolean updateNode(NodeImpl item, float minX, float minY, float maxX, flo if (obj != null) { obj.updateBoundaries(minX, minY, maxX, maxY); quadTreeRoot.update(item); + version++; return true; } else { return false; @@ -86,6 +143,7 @@ public boolean addNode(NodeImpl item) { spatialData = new SpatialNodeDataImpl(minX, minY, maxX, maxY); item.setSpatialData(spatialData); quadTreeRoot.insert(item); + version++; return true; } else { return false; @@ -100,9 +158,10 @@ public void clear() { try { for (Node node : getAllNodes()) { SpatialNodeDataImpl spatialData = ((NodeImpl) node).getSpatialData(); - spatialData.setQuadTreeNode(null); + spatialData.clear(); } quadTreeRoot.clear(); + version++; } finally { writeUnlock(); } @@ -114,6 +173,7 @@ public boolean removeNode(NodeImpl item) { final SpatialNodeDataImpl spatialData = item.getSpatialData(); if (spatialData != null && spatialData.quadTreeNode != null) { quadTreeRoot.delete(item, true); + version++; return true; } return false; @@ -169,10 +229,21 @@ public int getDepth() { return depth; } + public int getNodeCount(boolean keepOnlyWithObjects) { + readLock(); + int count = quadTreeRoot.getNodeCount(keepOnlyWithObjects); + readUnlock(); + return count; + } + public Rect2D getBoundaries() { + return getBoundaries(null); + } + + public Rect2D getBoundaries(Predicate predicate) { readLock(); try { - NodeIterable allNodes = getAllNodes(); + NodeIterable allNodes = predicate == null ? getAllNodes() : getAllNodes(predicate); float minX = Float.POSITIVE_INFINITY; float minY = Float.POSITIVE_INFINITY; @@ -182,6 +253,9 @@ public Rect2D getBoundaries() { boolean hasNodes = false; for (Node node : allNodes) { + if (node == null) { + continue; + } SpatialNodeDataImpl spatialData = ((NodeImpl) node).getSpatialData(); if (spatialData != null) { hasNodes = true; @@ -207,13 +281,37 @@ public Rect2D getBoundaries() { } } + private int collectOverlapping(QuadTreeNode node, Rect2D searchRect, Set resultSet) { + if (searchRect != null && !node.rect.intersects(searchRect)) { + return 0; + } + + // If this node has objects and intersects with search rect, add it + int nodeCount = 0; + if (node.objectCount > 0) { + resultSet.add(node); + nodeCount += node.objectCount; + } + + // Recursively check children + if (node.childTL != null) { + nodeCount += collectOverlapping(node.childTL, searchRect, resultSet); + nodeCount += collectOverlapping(node.childTR, searchRect, resultSet); + nodeCount += collectOverlapping(node.childBL, searchRect, resultSet); + nodeCount += collectOverlapping(node.childBR, searchRect, resultSet); + } + return nodeCount; + } + protected class QuadTreeNode { - private Set objects = null; + private NodeImpl[] objects = null; // Fixed-size array for objects + private int objectCount = 0; // Number of objects currently in this node private final Rect2D rect; // The area this QuadTree represents private final QuadTreeNode parent; // The parent of this quad private final int level; + private int size = 0; // Total number of objects in this node and its children private QuadTreeNode childTL = null; // Top Left Child private QuadTreeNode childTR = null; // Top Right Child @@ -245,11 +343,11 @@ public QuadTreeNode parent() { } public int count() { - return objectCount(); + return size; } public boolean isEmptyLeaf() { - return count() == 0 && childTL == null; + return size == 0 && childTL == null; } public QuadTreeNode(Rect2D rect) { @@ -264,36 +362,70 @@ private QuadTreeNode(QuadTreeNode parent, int level, Rect2D rect) { private void add(NodeImpl item) { if (objects == null) { - objects = new LinkedHashSet<>(); + // Allocate initial array + objects = new NodeImpl[maxObjectsPerNode / 16]; } - item.getSpatialData().setQuadTreeNode(this); - objects.add(item); - } + // Check if item is already in this node (avoid duplicates) + SpatialNodeDataImpl spatialData = item.getSpatialData(); + if (spatialData.quadTreeNode == this && spatialData.arrayIndex >= 0) { + return; // Already in this node + } - private void remove(NodeImpl item) { - if (objects != null) { - objects.remove(item); + // Resize array if needed (can happen when at max depth or when objects don't + // fit in children) + if (objectCount >= objects.length) { + NodeImpl[] newArray = new NodeImpl[objects.length * 2]; + System.arraycopy(objects, 0, newArray, 0, objects.length); + objects = newArray; + } + + // Add to array + objects[objectCount] = item; + spatialData.setQuadTreeNode(this); + spatialData.setArrayIndex(objectCount); + objectCount++; + + // Update size and edge size for this node and all parents + QuadTreeNode node = this; + while (node != null) { + node.size++; + node = node.parent; } } - private int objectCount() { - int count = 0; + private void remove(NodeImpl item) { + if (objects != null && objectCount > 0) { + SpatialNodeDataImpl spatialData = item.getSpatialData(); + int index = spatialData.arrayIndex; + + if (index >= 0 && index < objectCount && objects[index] == item) { + // Swap with last element for O(1) removal + objectCount--; + NodeImpl lastItem = objects[objectCount]; + objects[index] = lastItem; + objects[objectCount] = null; + + // Update the moved item's index + if (lastItem != null && index < objectCount) { + lastItem.getSpatialData().setArrayIndex(index); + } - // add the objects at this level - if (objects != null) { - count += objects.size(); - } + // Clear removed item's data + spatialData.clear(); - // add the objects that are contained in the children - if (childTL != null) { - count += childTL.objectCount(); - count += childTR.objectCount(); - count += childBL.objectCount(); - count += childBR.objectCount(); + // Update size + QuadTreeNode node = this; + while (node != null) { + node.size--; + node = node.parent; + } + } } + } - return count; + private int objectCount() { + return size; } private void subdivide() { @@ -311,19 +443,40 @@ private void subdivide() { childBL = new QuadTreeNode(this, level + 1, new Rect2D(minX, halfY, halfX, maxY)); childBR = new QuadTreeNode(this, level + 1, new Rect2D(halfX, halfY, maxX, maxY)); + // Keep track of objects that couldn't be moved + NodeImpl[] remainingObjects = new NodeImpl[objectCount]; + int remainingCount = 0; + // If they're completely contained by the quad, bump objects down - final Iterator iterator = objects.iterator(); - while (iterator.hasNext()) { - NodeImpl obj = iterator.next(); + for (int i = 0; i < objectCount; i++) { + NodeImpl obj = objects[i]; QuadTreeNode destTree = getDestinationTree(obj); if (destTree != this) { - // Insert to the appropriate tree, remove the object, and - // back up one in the loop + // Insert to the appropriate tree destTree.insert(obj); - iterator.remove(); + // Update size + QuadTreeNode node = this; + while (node != null) { + node.size--; + node = node.parent; + } + } else { + // Keep this object in the current node + remainingObjects[remainingCount] = obj; + obj.getSpatialData().setArrayIndex(remainingCount); + remainingCount++; } } + + // Update this node's object array + for (int i = 0; i < remainingCount; i++) { + objects[i] = remainingObjects[i]; + } + for (int i = remainingCount; i < objectCount; i++) { + objects[i] = null; + } + objectCount = remainingCount; } private QuadTreeNode getDestinationTree(NodeImpl item) { @@ -412,10 +565,21 @@ private void clear() { // clear any objects at this level if (objects != null) { - objects.clear(); + // Clear spatial data references for all objects + for (int i = 0; i < objectCount; i++) { + if (objects[i] != null) { + SpatialNodeDataImpl spatialData = objects[i].getSpatialData(); + spatialData.clear(); + objects[i] = null; + } + } objects = null; + objectCount = 0; } + // Reset size and edge size + size = 0; + // Set the children to null childTL = null; childTR = null; @@ -452,8 +616,7 @@ private void insert(NodeImpl item) { } } - if (objects == null || (childTL == null && (level >= maxLevels || objects - .size() + 1 <= maxObjectsPerNode))) { + if (objects == null || (childTL == null && (level >= maxLevels || objectCount + 1 <= maxObjectsPerNode))) { // If there's room to add the object, just add it add(item); } else { @@ -476,10 +639,38 @@ private NodeIterable getNodes(Rect2D searchRect) { return new QuadTreeNodesIterable(searchRect); } + private NodeIterable getNodes(Rect2D searchRect, boolean approximate) { + return new QuadTreeNodesIterable(searchRect, approximate); + } + + private NodeIterable getNodes(Rect2D searchRect, boolean approximate, Predicate predicate) { + return new FilteredQuadTreeNodeIterable(searchRect, approximate, predicate); + } + private NodeIterable getAllNodes() { return new QuadTreeNodesIterable(null); } + private NodeIterable getAllNodes(Predicate predicate) { + return new FilteredQuadTreeNodeIterable(null, false, predicate); + } + + private EdgeIterable getEdges(Rect2D searchRect) { + return new QuadTreeEdgesIterable(searchRect); + } + + private EdgeIterable getEdges(Rect2D searchRect, boolean approximate) { + return new QuadTreeEdgesIterable(searchRect, approximate); + } + + private EdgeIterable getEdges(Rect2D searchRect, boolean approximate, Predicate predicate) { + return new FilteredQuadTreeEdgeIterable(searchRect, approximate, predicate); + } + + private EdgeIterable getAllEdges() { + return new QuadTreeEdgesIterable(null); + } + private void update(NodeImpl item) { SpatialNodeDataImpl spatialData = item.getSpatialData(); if (spatialData.quadTreeNode != null) { @@ -500,6 +691,25 @@ private int getDepth() { return maxLevel; } + private int getNodeCount(boolean withObjects) { + int count = 1; // Count this node + + // If withObjects is true, only count nodes that have objects + if (withObjects && (objects == null || objectCount == 0)) { + count = 0; + } + + // Recursively count children + if (childTL != null) { + count += childTL.getNodeCount(withObjects); + count += childTR.getNodeCount(withObjects); + count += childBL.getNodeCount(withObjects); + count += childBR.getNodeCount(withObjects); + } + + return count; + } + public void toString(StringBuilder sb) { for (int i = 0; i < level; i++) { sb.append(" "); @@ -507,13 +717,11 @@ public void toString(StringBuilder sb) { sb.append(rect.toString()).append('\n'); if (objects != null) { - for (NodeImpl object : objects) { - for (int i = 0; i <= level; i++) { - sb.append(" "); - } - - sb.append(object.getId()).append('\n'); + for (int j = 0; j <= level; j++) { + sb.append(" "); } + + sb.append(objectCount).append(" objects \n"); } if (childTL != null) { @@ -525,23 +733,53 @@ public void toString(StringBuilder sb) { } } + private class FilteredQuadTreeNodeIterable extends QuadTreeNodesIterable { + + private final Predicate predicate; + + public FilteredQuadTreeNodeIterable(Rect2D searchRect, boolean approximate, Predicate predicate) { + super(searchRect, approximate); + this.predicate = predicate; + } + + @Override + public Iterator iterator() { + return new QuadTreeNodesIterator(quadTreeRoot, searchRect, approximate, predicate); + } + + @Override + public Spliterator spliterator() { + return new FilteredQuadTreeNodesSpliterator(quadTreeRoot, searchRect, approximate, predicate); + } + } + private class QuadTreeNodesIterable implements NodeIterable { - private final Rect2D searchRect; + protected final Rect2D searchRect; + protected final boolean approximate; public QuadTreeNodesIterable(Rect2D searchRect) { + this(searchRect, GraphStoreConfiguration.SPATIAL_INDEX_APPROXIMATE_AREA_SEARCH); + } + + public QuadTreeNodesIterable(Rect2D searchRect, boolean approximate) { this.searchRect = searchRect; + this.approximate = approximate; } @Override public Iterator iterator() { - return new QuadTreeNodesIterator(quadTreeRoot, searchRect); + return new QuadTreeNodesIterator(quadTreeRoot, searchRect, approximate); + } + + @Override + public Spliterator spliterator() { + return new QuadTreeNodesSpliterator(quadTreeRoot, searchRect, approximate); } @Override public Node[] toArray() { - final Collection collection = toCollection(); - return collection.toArray(new Node[0]); + return toCollection().toArray(new Node[0]); } @Override @@ -570,12 +808,183 @@ public Set toSet() { public void doBreak() { readUnlock(); } + } + + private class FilteredQuadTreeEdgeIterable extends QuadTreeEdgesIterable { + + private final Predicate predicate; + + public FilteredQuadTreeEdgeIterable(Rect2D searchRect, boolean approximate, Predicate predicate) { + super(searchRect, approximate); + this.predicate = predicate; + } + + @Override + public Iterator iterator() { + return new QuadTreeEdgesIterator(quadTreeRoot, searchRect, approximate, predicate); + } + + @Override + public Spliterator spliterator() { + HashSet overlappingNodes = new HashSet<>(); + int nodeCount = collectOverlapping(quadTreeRoot, searchRect, overlappingNodes); + if (useDirectIterator(nodeCount)) { + return new QuadTreeGlobalEdgesSpliterator(searchRect, approximate, overlappingNodes, predicate); + } + // Use local iterator + return new FilteredQuadTreeEdgesSpliterator(quadTreeRoot, searchRect, approximate, predicate); + } + } + + private class QuadTreeEdgesIterable implements EdgeIterable { + + protected final Rect2D searchRect; + protected final boolean approximate; + + public QuadTreeEdgesIterable(Rect2D searchRect) { + this(searchRect, GraphStoreConfiguration.SPATIAL_INDEX_APPROXIMATE_AREA_SEARCH); + } + + public QuadTreeEdgesIterable(Rect2D searchRect, boolean approximate) { + this.searchRect = searchRect; + this.approximate = approximate; + } + + @Override + public Iterator iterator() { + return new QuadTreeEdgesIterator(quadTreeRoot, searchRect, approximate); + } + + protected boolean useDirectIterator(int nodeCount) { + return (float) nodeCount / quadTreeRoot.size > GraphStoreConfiguration.SPATIAL_INDEX_LOCAL_ITERATOR_THRESHOLD; + } + + @Override + public Spliterator spliterator() { + if (searchRect == null) { + // Special case: all edges + return new QuadTreeGlobalEdgesSpliterator(null, approximate, null, null); + } + HashSet overlappingNodes = new HashSet<>(); + int nodeCount = collectOverlapping(quadTreeRoot, searchRect, overlappingNodes); + if (approximate && nodeCount == quadTreeRoot.size) { + // Optimisation: approximate search and all nodes overlapping, so just return + // all edges + return new QuadTreeGlobalEdgesSpliterator(null, true, null, null); + } else if (useDirectIterator(nodeCount)) { + return new QuadTreeGlobalEdgesSpliterator(searchRect, approximate, overlappingNodes, null); + } + // Use local iterator + return new QuadTreeEdgesSpliterator(quadTreeRoot, searchRect, approximate); + } + + @Override + public Edge[] toArray() { + return toCollection().toArray(new Edge[0]); + } + + @Override + public Collection toCollection() { + final List list = new ArrayList<>(); + + for (Edge edge : this) { + list.add(edge); + } + + return list; + } + + @Override + public Set toSet() { + final Set set = new HashSet<>(); + + for (Edge edge : this) { + set.add(edge); + } + + return set; + } + + @Override + public void doBreak() { + readUnlock(); + } + + } + + private class QuadTreeEdgesIterator implements Iterator { + + private final EdgeStore.EdgeInOutMultiIterator edgeIterator; + private final Predicate predicate; + private boolean finished = false; + private Edge next; + + public QuadTreeEdgesIterator(QuadTreeNode root, Rect2D searchRect, boolean approximate) { + this(root, searchRect, approximate, null); + } + + public QuadTreeEdgesIterator(QuadTreeNode root, Rect2D searchRect, boolean approximate, Predicate predicate) { + this.predicate = predicate; + readLock(); + + // Create a node iterator for the quad tree + final QuadTreeNodesIterator nodeIterator = new QuadTreeNodesIterator(root, searchRect, approximate); + + // Create the edge iterator using the EdgeStore method + this.edgeIterator = graphStore.edgeStore.edgeIterator(new Iterator<>() { + @Override + public boolean hasNext() { + return nodeIterator.hasNext(); + } + + @Override + public NodeImpl next() { + return nodeIterator.next(); + } + }, true); + } + + @Override + public boolean hasNext() { + if (finished) { + return false; + } + + if (next != null) { + return true; + } + + // Look for next edge that passes predicate + while (edgeIterator != null && edgeIterator.hasNext()) { + Edge edge = edgeIterator.next(); + if (predicate == null || predicate.test(edge)) { + next = edge; + return true; + } + } + + readUnlock(); + finished = true; + return false; + } + + @Override + public Edge next() { + if (next == null && !hasNext()) { + throw new IllegalStateException("No next available!"); + } + Edge result = next; + next = null; + return result; + } } private class QuadTreeNodesIterator implements Iterator { private final Rect2D searchRect; + private final boolean approximate; + private final Predicate predicate; private final Deque nodesStack = new ArrayDeque<>(); private final Deque fullyContainedStack = new ArrayDeque<>(); @@ -586,8 +995,14 @@ private class QuadTreeNodesIterator implements Iterator { private NodeImpl next; - public QuadTreeNodesIterator(QuadTreeNode root, Rect2D searchRect) { + public QuadTreeNodesIterator(QuadTreeNode root, Rect2D searchRect, boolean approximate) { + this(root, searchRect, approximate, null); + } + + public QuadTreeNodesIterator(QuadTreeNode root, Rect2D searchRect, boolean approximate, Predicate predicate) { this.searchRect = searchRect; + this.approximate = approximate; + this.predicate = predicate; readLock(); @@ -598,7 +1013,7 @@ public QuadTreeNodesIterator(QuadTreeNode root, Rect2D searchRect) { // contained, to correctly handle the case of nodes out of the quad // tree bounds addChildrenToVisit(root, currentFullyContained); - currentIterator = root.objects != null ? root.objects.iterator() : null; + currentIterator = root.objects != null ? new ArrayIterator(root.objects, root.objectCount) : null; } private void addChildrenToVisit(QuadTreeNode quadTreeNode, boolean fullyContained) { @@ -629,10 +1044,21 @@ public boolean hasNext() { if (currentIterator != null) { while (currentIterator.hasNext()) { final NodeImpl elem = currentIterator.next(); - final SpatialNodeDataImpl spatialData = elem.getSpatialData(); - if (currentFullyContained || searchRect - .intersects(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY)) { + // First check spatial conditions + boolean spatialMatch; + if (approximate || currentFullyContained) { + // In approximate mode or when fully contained, include all objects + spatialMatch = true; + } else { + // In exact mode, check intersection + final SpatialNodeDataImpl spatialData = elem.getSpatialData(); + spatialMatch = searchRect + .intersects(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY); + } + + // If spatial conditions are met, check predicate + if (spatialMatch && (predicate == null || predicate.test(elem))) { next = elem; return true; } @@ -646,7 +1072,8 @@ public boolean hasNext() { if (currentFullyContained || pointer.rect.intersects(searchRect)) { addChildrenToVisit(pointer, currentFullyContained); - currentIterator = pointer.objects != null ? pointer.objects.iterator() : null; + currentIterator = pointer.objects != null + ? new ArrayIterator(pointer.objects, pointer.objectCount) : null; } else { currentIterator = null; } @@ -667,9 +1094,633 @@ public NodeImpl next() { final NodeImpl node = next; next = null; - return node; } } + + // Helper class to iterate over array elements + private static class ArrayIterator implements Iterator { + private final NodeImpl[] array; + private final int size; + private final Predicate predicate; + private int index = 0; + private NodeImpl next; + + public ArrayIterator(NodeImpl[] array, int size) { + this(array, size, null); + } + + public ArrayIterator(NodeImpl[] array, int size, Predicate predicate) { + this.array = array; + this.size = size; + this.predicate = predicate; + } + + @Override + public boolean hasNext() { + if (next != null) { + return true; + } + + // Find next element that passes predicate + while (index < size) { + NodeImpl candidate = array[index++]; + if (predicate == null || predicate.test(candidate)) { + next = candidate; + return true; + } + } + return false; + } + + @Override + public NodeImpl next() { + if (next == null && !hasNext()) { + throw new IllegalStateException("No more elements"); + } + NodeImpl result = next; + next = null; + return result; + } + } + + private abstract class AbstractQuadTreeSpliterator implements Spliterator { + protected final Rect2D searchRect; + protected final boolean approximate; + protected final Deque nodesStack = new ArrayDeque<>(); + protected final Deque fullyContainedStack = new ArrayDeque<>(); + + protected final int expectedVersion; + protected Iterator currentIterator; + protected boolean currentFullyContained; + protected T next; + protected int remainingSize; + + protected AbstractQuadTreeSpliterator(QuadTreeNode root, Rect2D searchRect, boolean approximate) { + this.searchRect = searchRect; + this.approximate = approximate; + this.expectedVersion = version; + + // Null rect means get all + currentFullyContained = searchRect == null; + + // Initialize with root + addNode(root, currentFullyContained); + currentIterator = createIteratorForNode(root); + + // For SIZED characteristic, we need exact count + if (searchRect == null) { + // Getting all elements, so we can use the maintained size + remainingSize = root.size; + } else if (approximate) { + // In approximate mode, count all elements in intersecting quadrants + remainingSize = countNodesInRectApproximate(root, searchRect); + } else { + // Need to count elements in the search rect + remainingSize = countNodesInRect(root, searchRect); + } + } + + protected AbstractQuadTreeSpliterator(QuadTreeNode node, Rect2D searchRect, boolean approximate, int expectedVersion, boolean fullyContained, int size) { + this.searchRect = searchRect; + this.approximate = approximate; + this.expectedVersion = expectedVersion; + this.remainingSize = size; + this.currentFullyContained = fullyContained; + + if (node != null) { + addNode(node, fullyContained); + currentIterator = createIteratorForNode(node); + } else { + currentIterator = null; + } + } + + protected void checkForComodification() { + if (version != expectedVersion) { + throw new ConcurrentModificationException(); + } + } + + protected void addNode(QuadTreeNode node, boolean fullyContained) { + if (node.childTL != null) { + nodesStack.push(node.childBR); + nodesStack.push(node.childBL); + nodesStack.push(node.childTR); + nodesStack.push(node.childTL); + + fullyContainedStack.push(fullyContained); + fullyContainedStack.push(fullyContained); + fullyContainedStack.push(fullyContained); + fullyContainedStack.push(fullyContained); + } + } + + protected int countNodesInRect(QuadTreeNode node, Rect2D rect) { + int count = 0; + + // Count objects at this level + if (node.objects != null) { + for (int i = 0; i < node.objectCount; i++) { + NodeImpl obj = node.objects[i]; + SpatialNodeDataImpl spatialData = obj.getSpatialData(); + if (rect.intersects(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY)) { + count++; + } + } + } + + // Count in children if they intersect + if (node.childTL != null) { + if (rect.contains(node.childTL.rect)) { + count += node.childTL.size; + } else if (node.childTL.rect.intersects(rect)) { + count += countNodesInRect(node.childTL, rect); + } + + if (rect.contains(node.childTR.rect)) { + count += node.childTR.size; + } else if (node.childTR.rect.intersects(rect)) { + count += countNodesInRect(node.childTR, rect); + } + + if (rect.contains(node.childBL.rect)) { + count += node.childBL.size; + } else if (node.childBL.rect.intersects(rect)) { + count += countNodesInRect(node.childBL, rect); + } + + if (rect.contains(node.childBR.rect)) { + count += node.childBR.size; + } else if (node.childBR.rect.intersects(rect)) { + count += countNodesInRect(node.childBR, rect); + } + } + + return count; + } + + protected int countNodesInRectApproximate(QuadTreeNode node, Rect2D rect) { + int count = 0; + + // Count objects at this level if the node intersects + if (node.objects != null) { + count += node.objectCount; + } + + // Count in children if they intersect + if (node.childTL != null) { + if (rect.containsOrIntersects(node.childTL.rect)) { + count += node.childTL.size; + } + if (rect.containsOrIntersects(node.childTR.rect)) { + count += node.childTR.size; + } + if (rect.containsOrIntersects(node.childBL.rect)) { + count += node.childBL.size; + } + if (rect.containsOrIntersects(node.childBR.rect)) { + count += node.childBR.size; + } + } + + return count; + } + + @Override + public boolean tryAdvance(Consumer action) { + checkForComodification(); + + if (next != null || findNext()) { + action.accept(next); + next = null; + remainingSize--; + return true; + } + return false; + } + + protected abstract boolean findNext(); + + protected abstract Iterator createIteratorForNode(QuadTreeNode node); + + protected abstract boolean checkElementSpatialMatch(Object element); + + protected abstract AbstractQuadTreeSpliterator createSplitInstance(QuadTreeNode node, Rect2D searchRect, boolean approximate, int expectedVersion, boolean fullyContained, int size); + + @Override + public Spliterator trySplit() { + checkForComodification(); + + // Can only split if we have nodes on the stack + if (!nodesStack.isEmpty() && remainingSize > 1) { + // Take half of the remaining nodes from the stack + int nodesToSplit = Math.min(nodesStack.size() / 2, remainingSize / 2); + if (nodesToSplit > 0) { + Deque splitNodes = new ArrayDeque<>(); + Deque splitContained = new ArrayDeque<>(); + + // Calculate size for the split + int splitSize = 0; + + // Move nodes to split queues and calculate their size + for (int i = 0; i < nodesToSplit; i++) { + QuadTreeNode node = nodesStack.removeLast(); + boolean contained = fullyContainedStack.removeLast(); + splitNodes.addFirst(node); + splitContained.addFirst(contained); + + if (searchRect == null || contained) { + splitSize += node.size; + } else if (approximate) { + splitSize += countNodesInRectApproximate(node, searchRect); + } else { + splitSize += countNodesInRect(node, searchRect); + } + } + + // Update our remaining size + remainingSize -= splitSize; + + // Create new spliterator for the split portion with empty initial state + AbstractQuadTreeSpliterator split = createSplitInstance(null, searchRect, approximate, expectedVersion, false, splitSize); + + // Add all split nodes to the new spliterator's stack + while (!splitNodes.isEmpty()) { + split.nodesStack.push(splitNodes.removeLast()); + split.fullyContainedStack.push(splitContained.removeLast()); + } + + return split; + } + } + return null; + } + + @Override + public long estimateSize() { + return remainingSize; + } + } + + private class QuadTreeNodesSpliterator extends AbstractQuadTreeSpliterator { + + public QuadTreeNodesSpliterator(QuadTreeNode root, Rect2D searchRect, boolean approximate) { + super(root, searchRect, approximate); + } + + private QuadTreeNodesSpliterator(QuadTreeNode node, Rect2D searchRect, boolean approximate, int expectedVersion, boolean fullyContained, int size) { + super(node, searchRect, approximate, expectedVersion, fullyContained, size); + } + + @Override + protected Iterator createIteratorForNode(QuadTreeNode node) { + return node.objects != null ? new ArrayIterator(node.objects, node.objectCount) : null; + } + + @Override + protected boolean checkElementSpatialMatch(Object element) { + NodeImpl elem = (NodeImpl) element; + if (approximate || currentFullyContained || searchRect == null) { + return true; + } else { + final SpatialNodeDataImpl spatialData = elem.getSpatialData(); + return searchRect.intersects(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY); + } + } + + @Override + protected AbstractQuadTreeSpliterator createSplitInstance(QuadTreeNode node, Rect2D searchRect, boolean approximate, int expectedVersion, boolean fullyContained, int size) { + return new QuadTreeNodesSpliterator(node, searchRect, approximate, expectedVersion, fullyContained, size); + } + + @Override + protected boolean findNext() { + while (currentIterator != null || !nodesStack.isEmpty()) { + if (currentIterator != null) { + while (currentIterator.hasNext()) { + final NodeImpl elem = (NodeImpl) currentIterator.next(); + + if (checkElementSpatialMatch(elem)) { + next = elem; + return true; + } + } + currentIterator = null; + } else { + final QuadTreeNode pointer = nodesStack.pop(); + currentFullyContained = fullyContainedStack + .pop() || (searchRect != null && searchRect.contains(pointer.rect)); + + if (currentFullyContained || searchRect == null || pointer.rect.intersects(searchRect)) { + addNode(pointer, currentFullyContained); + currentIterator = createIteratorForNode(pointer); + } + } + } + return false; + } + + @Override + public int characteristics() { + return DISTINCT | SIZED | SUBSIZED | NONNULL; + } + } + + private abstract static class FilteredSpliterator, P extends Spliterator> implements Spliterator { + protected final S parentSpliterator; + protected final Predicate predicate; + protected final Object[] holder = new Object[1]; + + protected FilteredSpliterator(S parentSpliterator, Predicate predicate) { + this.parentSpliterator = parentSpliterator; + this.predicate = predicate; + } + + protected abstract P createSplitInstance(S splitParent, Predicate predicate); + + protected abstract boolean testPredicate(T element); + + @Override + public boolean tryAdvance(Consumer action) { + while (true) { + boolean advanced = parentSpliterator.tryAdvance(e -> holder[0] = e); + if (!advanced) + return false; + @SuppressWarnings("unchecked") + T t = (T) holder[0]; + if (testPredicate(t)) { + action.accept(t); + return true; + } + } + } + + @Override + @SuppressWarnings("unchecked") + public Spliterator trySplit() { + S splitParent = (S) parentSpliterator.trySplit(); + if (splitParent != null) { + return createSplitInstance(splitParent, predicate); + } + return null; + } + + @Override + public long estimateSize() { + return parentSpliterator.estimateSize(); + } + + @Override + public int characteristics() { + return parentSpliterator.characteristics() & ~(Spliterator.SIZED | Spliterator.SUBSIZED); + } + } + + private class FilteredQuadTreeNodesSpliterator extends FilteredSpliterator { + + public FilteredQuadTreeNodesSpliterator(QuadTreeNode root, Rect2D searchRect, boolean approximate, Predicate predicate) { + super(new QuadTreeNodesSpliterator(root, searchRect, approximate), predicate); + } + + private FilteredQuadTreeNodesSpliterator(QuadTreeNodesSpliterator parentSpliterator, Predicate predicate) { + super(parentSpliterator, predicate); + } + + @Override + protected FilteredQuadTreeNodesSpliterator createSplitInstance(QuadTreeNodesSpliterator splitParent, Predicate predicate) { + return new FilteredQuadTreeNodesSpliterator(splitParent, predicate); + } + + @Override + protected boolean testPredicate(Node element) { + return predicate == null || predicate.test(element); + } + } + + private class QuadTreeEdgesSpliterator extends AbstractQuadTreeSpliterator { + + public QuadTreeEdgesSpliterator(QuadTreeNode root, Rect2D searchRect) { + this(root, searchRect, false); + } + + public QuadTreeEdgesSpliterator(QuadTreeNode root, Rect2D searchRect, boolean approximate) { + super(root, searchRect, approximate); + } + + private QuadTreeEdgesSpliterator(QuadTreeNode node, Rect2D searchRect, boolean approximate, int expectedVersion, boolean fullyContained, int size) { + super(node, searchRect, approximate, expectedVersion, fullyContained, size); + } + + @Override + protected Iterator createIteratorForNode(QuadTreeNode node) { + if (node.objects == null) { + return Collections.emptyIterator(); + } + return graphStore.edgeStore.edgeIterator(new ArrayIterator(node.objects, node.objectCount), false); + } + + @Override + protected boolean checkElementSpatialMatch(Object element) { + Edge edge = (Edge) element; + if (approximate || currentFullyContained || searchRect == null) { + return true; + } else { + // In exact mode, check if edge endpoints intersect with search rect + Node source = edge.getSource(); + Node target = edge.getTarget(); + SpatialNodeDataImpl sourceSpatialData = ((NodeImpl) source).getSpatialData(); + SpatialNodeDataImpl targetSpatialData = ((NodeImpl) target).getSpatialData(); + + return (sourceSpatialData != null && searchRect + .intersects(sourceSpatialData.minX, sourceSpatialData.minY, sourceSpatialData.maxX, sourceSpatialData.maxY)) || (targetSpatialData != null && searchRect + .intersects(targetSpatialData.minX, targetSpatialData.minY, targetSpatialData.maxX, targetSpatialData.maxY)); + } + } + + @Override + protected AbstractQuadTreeSpliterator createSplitInstance(QuadTreeNode node, Rect2D searchRect, boolean approximate, int expectedVersion, boolean fullyContained, int size) { + return new QuadTreeEdgesSpliterator(node, searchRect, approximate, expectedVersion, fullyContained, size); + } + + @Override + protected boolean findNext() { + while (currentIterator != null || !nodesStack.isEmpty()) { + if (currentIterator != null) { + if (currentIterator.hasNext()) { + Edge edge = (Edge) currentIterator.next(); + + if (checkElementSpatialMatch(edge)) { + next = edge; + return true; + } + } else { + currentIterator = null; + } + } else { + final QuadTreeNode pointer = nodesStack.pop(); + currentFullyContained = fullyContainedStack + .pop() || (searchRect != null && searchRect.contains(pointer.rect)); + + if (currentFullyContained || searchRect == null || pointer.rect.intersects(searchRect)) { + addNode(pointer, currentFullyContained); + currentIterator = createIteratorForNode(pointer); + } + } + } + return false; + } + + @Override + public int characteristics() { + return NONNULL; + } + } + + private class FilteredQuadTreeEdgesSpliterator extends FilteredSpliterator { + + public FilteredQuadTreeEdgesSpliterator(QuadTreeNode root, Rect2D searchRect, boolean approximate, Predicate predicate) { + super(new QuadTreeEdgesSpliterator(root, searchRect, approximate), predicate); + } + + private FilteredQuadTreeEdgesSpliterator(QuadTreeEdgesSpliterator parentSpliterator, Predicate predicate) { + super(parentSpliterator, predicate); + } + + @Override + protected FilteredQuadTreeEdgesSpliterator createSplitInstance(QuadTreeEdgesSpliterator splitParent, Predicate predicate) { + return new FilteredQuadTreeEdgesSpliterator(splitParent, predicate); + } + + @Override + protected boolean testPredicate(Edge element) { + return predicate == null || predicate.test(element); + } + } + + /** + * A spliterator that iterates through all edges in the EdgeStore and filters + * them based on whether their nodes belong to quad tree nodes that overlap with + * a search rectangle. This approach iterates edges directly rather than + * iterating nodes first. + */ + protected class QuadTreeGlobalEdgesSpliterator implements Spliterator { + + private final Rect2D searchRect; + private final boolean approximate; + private final Set overlappingQuadNodes; + private final Spliterator baseSpliterator; + private final int expectedVersion; + private final Predicate additionalPredicate; + + public QuadTreeGlobalEdgesSpliterator(Rect2D searchRect, boolean approximate, Set overlappingQuadNodes, Predicate additionalPredicate) { + this.searchRect = searchRect; + this.approximate = approximate; + this.additionalPredicate = additionalPredicate; + this.expectedVersion = version; + this.overlappingQuadNodes = overlappingQuadNodes; + + // Create the base spliterator from EdgeStore with our predicate + if (additionalPredicate == null) { + if (searchRect == null) { + // No filtering needed, use the full spliterator + this.baseSpliterator = graphStore.edgeStore.spliterator(); + } else { + // Only spatial filtering + this.baseSpliterator = graphStore.edgeStore.newFilteredSpliterator(this::shouldIncludeEdge); + } + } else { + if (searchRect == null) { + this.baseSpliterator = graphStore.edgeStore + .newFilteredSpliterator(this::shouldIncludeEdgeAllWithPredicate); + } else { + this.baseSpliterator = graphStore.edgeStore.newFilteredSpliterator(this::shouldIncludeEdge); + } + } + } + + private QuadTreeGlobalEdgesSpliterator(Rect2D searchRect, boolean approximate, Set overlappingQuadNodes, Spliterator baseSpliterator, int expectedVersion, Predicate additionalPredicate) { + this.searchRect = searchRect; + this.approximate = approximate; + this.overlappingQuadNodes = overlappingQuadNodes; + this.baseSpliterator = baseSpliterator; + this.expectedVersion = expectedVersion; + this.additionalPredicate = additionalPredicate; + } + + private boolean shouldIncludeEdgeAllWithPredicate(EdgeImpl edge) { + checkForComodification(); + + return additionalPredicate.test(edge); + } + + /** + * Determines if an edge should be included based on spatial filtering criteria + */ + private boolean shouldIncludeEdge(EdgeImpl edge) { + checkForComodification(); + + boolean spatialMatch = false; + + SpatialNodeDataImpl sourceSpatialData = edge.source.getSpatialData(); + SpatialNodeDataImpl targetSpatialData = edge.target.getSpatialData(); + + if (sourceSpatialData != null && sourceSpatialData.quadTreeNode != null) { + spatialMatch = overlappingQuadNodes.contains(sourceSpatialData.quadTreeNode); + } + + if (!spatialMatch && targetSpatialData != null && targetSpatialData.quadTreeNode != null) { + // Only check target if source wasn't already overlapping + spatialMatch = overlappingQuadNodes.contains(targetSpatialData.quadTreeNode); + } + + // Apply additional predicate if provided + if (spatialMatch && (additionalPredicate == null || additionalPredicate.test(edge))) { + if (approximate) { + return true; + } else { + // In exact mode, check if edge endpoints intersect with search rect + boolean sourceIntersects = sourceSpatialData != null && searchRect + .intersects(sourceSpatialData.minX, sourceSpatialData.minY, sourceSpatialData.maxX, sourceSpatialData.maxY); + boolean targetIntersects = targetSpatialData != null && searchRect + .intersects(targetSpatialData.minX, targetSpatialData.minY, targetSpatialData.maxX, targetSpatialData.maxY); + return sourceIntersects || targetIntersects; + } + } + return false; + } + + private void checkForComodification() { + if (expectedVersion != version) { + throw new ConcurrentModificationException(); + } + } + + @Override + public boolean tryAdvance(Consumer action) { + return baseSpliterator.tryAdvance(action); + } + + @Override + public Spliterator trySplit() { + Spliterator splitBase = baseSpliterator.trySplit(); + if (splitBase == null) { + return null; + } + + return new QuadTreeGlobalEdgesSpliterator(searchRect, approximate, overlappingQuadNodes, splitBase, + expectedVersion, additionalPredicate); + } + + @Override + public long estimateSize() { + return baseSpliterator.estimateSize(); + } + + @Override + public int characteristics() { + return baseSpliterator.characteristics(); + } + } } diff --git a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java index 1a4152b4..7eebbff4 100644 --- a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java @@ -1,6 +1,6 @@ package org.gephi.graph.impl; -import java.util.Iterator; +import java.util.function.Predicate; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Node; @@ -15,30 +15,68 @@ */ public class SpatialIndexImpl implements SpatialIndex { - private final GraphStore store; protected final NodesQuadTree nodesTree; public SpatialIndexImpl(GraphStore store) { - this.store = store; float boundaries = GraphStoreConfiguration.SPATIAL_INDEX_DIMENSION_BOUNDARY; - this.nodesTree = new NodesQuadTree(new Rect2D(-boundaries, -boundaries, boundaries, boundaries)); + this.nodesTree = new NodesQuadTree(store, + new Rect2D(-boundaries / 2, -boundaries / 2, boundaries / 2, boundaries / 2)); } @Override public NodeIterable getNodesInArea(Rect2D rect) { - return nodesTree.getNodes(rect); + return nodesTree.getNodes(rect, false); + } + + @Override + public NodeIterable getApproximateNodesInArea(Rect2D rect) { + return nodesTree.getNodes(rect, true); } @Override public EdgeIterable getEdgesInArea(Rect2D rect) { - return new EdgeIterableWrapper(() -> new EdgeIterator(rect, nodesTree.getNodes(rect).iterator()), - nodesTree.lock); + return nodesTree.getEdges(rect, false); + } + + @Override + public EdgeIterable getApproximateEdgesInArea(Rect2D rect) { + return nodesTree.getEdges(rect, true); + } + + @Override + public void spatialIndexReadLock() { + nodesTree.readLock(); + } + + @Override + public void spatialIndexReadUnlock() { + nodesTree.readUnlock(); + } + + public NodeIterable getNodesInArea(Rect2D rect, Predicate predicate) { + return nodesTree.getNodes(rect, false, predicate); + } + + public NodeIterable getApproximateNodesInArea(Rect2D rect, Predicate predicate) { + return nodesTree.getNodes(rect, true, predicate); + } + + public EdgeIterable getEdgesInArea(Rect2D rect, Predicate predicate) { + return nodesTree.getEdges(rect, false, predicate); + } + + public EdgeIterable getApproximateEdgesInArea(Rect2D rect, Predicate predicate) { + return nodesTree.getEdges(rect, true, predicate); } protected void clearNodes() { nodesTree.clear(); } + protected void incrementVersion() { + nodesTree.incrementVersion(); + } + protected void addNode(final NodeImpl node) { nodesTree.addNode(node); } @@ -65,62 +103,11 @@ public Rect2D getBoundaries() { return nodesTree.getBoundaries(); } - protected class EdgeIterator implements Iterator { - - private final Iterator nodeItr; - private final Rect2D rect2D; - private Iterator edgeItr; - private Edge pointer; - private Node node; - - public EdgeIterator(Rect2D rect2D, Iterator nodeIterator) { - this.nodeItr = nodeIterator; - this.rect2D = rect2D; - - nodesTree.readLock(); - } - - @Override - public boolean hasNext() { - while (pointer == null) { - while (pointer == null && edgeItr != null && edgeItr.hasNext()) { - pointer = edgeItr.next(); - if (!pointer.isSelfLoop()) { - Node oppositeNode = store.getOpposite(node, pointer); - // Skip edge - do not return same edges twice when both - // source and target nodes are visible - SpatialNodeDataImpl spatialData = ((NodeImpl) oppositeNode).getSpatialData(); - if (oppositeNode.getStoreId() < node.getStoreId() && rect2D - .intersects(spatialData.minX, spatialData.minY, spatialData.maxX, spatialData.maxY)) { - pointer = null; - } - } - } - if (pointer == null) { - edgeItr = null; - if (nodeItr != null && nodeItr.hasNext()) { - node = nodeItr.next(); - edgeItr = store.edgeStore.edgeIterator(node); - } else { - nodesTree.readUnlock(); - return false; - } - } - } - - return true; - } - - @Override - public Edge next() { - Edge res = pointer; - pointer = null; - return res; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Not supported."); - } + public Rect2D getBoundaries(Predicate predicate) { + return nodesTree.getBoundaries(predicate); + } + + public int getObjectCount() { + return nodesTree.getObjectCount(); } } diff --git a/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java b/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java index cb4a04f9..9e62a5cc 100644 --- a/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java +++ b/src/main/java/org/gephi/graph/impl/SpatialNodeDataImpl.java @@ -5,6 +5,7 @@ public class SpatialNodeDataImpl { public float minX, minY, maxX, maxY; protected NodesQuadTree.QuadTreeNode quadTreeNode; + protected int arrayIndex = -1; // Index in the quad tree node's array, -1 if not in a node public SpatialNodeDataImpl(float minX, float minY, float maxX, float maxY) { this.minX = minX; @@ -23,4 +24,17 @@ public void updateBoundaries(float minX, float minY, float maxX, float maxY) { public void setQuadTreeNode(NodesQuadTree.QuadTreeNode quadTreeNode) { this.quadTreeNode = quadTreeNode; } + + public int getArrayIndex() { + return arrayIndex; + } + + public void setArrayIndex(int arrayIndex) { + this.arrayIndex = arrayIndex; + } + + public void clear() { + this.quadTreeNode = null; + this.arrayIndex = -1; + } } diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 055a4d5e..013d15ad 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -203,7 +203,7 @@ public NodeIterable getNeighbors(Node node, int type) { @Override public EdgeIterable getEdges(Node node) { - return new EdgeIterableWrapper(() -> store.edgeStore.edgeUndirectedIterator(node), store.getAutoLock()); + return new EdgeIterableWrapper(() -> store.edgeStore.edgeUndirectedIterator(node, true), store.getAutoLock()); } @Override diff --git a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java index 0498dfd3..205a5c33 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java @@ -859,7 +859,7 @@ public void testInOutIterator() { Object2ObjectMap outEdgeMap = getObjectMap(edges); for (NodeImpl n : getNodes(edges)) { - EdgeStore.EdgeInOutIterator itr = edgeStore.edgeIterator(n); + EdgeStore.EdgeInOutIterator itr = edgeStore.edgeIterator(n, true); for (; itr.hasNext();) { EdgeImpl e = itr.next(); if (e.isSelfLoop()) { @@ -881,13 +881,13 @@ public void testInOutIterator() { @Test(expectedExceptions = NullPointerException.class) public void testInOutIteratorNull() { EdgeStore edgeStore = new EdgeStore(); - edgeStore.edgeIterator(null); + edgeStore.edgeIterator((Node) null, true); } @Test(expectedExceptions = IllegalArgumentException.class) public void testInOutIteratorInvalid() { EdgeStore edgeStore = new EdgeStore(); - edgeStore.edgeIterator(new NodeImpl("0")); + edgeStore.edgeIterator(new NodeImpl("0"), true); } @Test @@ -903,7 +903,7 @@ public void testInOutIteratorAfterRemove() { Object2ObjectMap outEdgeMap = getObjectMap(edgeList.toArray(new EdgeImpl[0])); for (NodeImpl n : getNodes(edges)) { - EdgeStore.EdgeInOutIterator itr = edgeStore.edgeIterator(n); + EdgeStore.EdgeInOutIterator itr = edgeStore.edgeIterator(n, true); for (; itr.hasNext();) { EdgeImpl e = itr.next(); if (e.isSelfLoop()) { @@ -930,7 +930,7 @@ public void testInOutIteratorRemove() { int index = 0; for (NodeImpl n : getNodes(edges)) { - EdgeStore.EdgeInOutIterator itr = edgeStore.edgeIterator(n); + EdgeStore.EdgeInOutIterator itr = edgeStore.edgeIterator(n, true); for (; itr.hasNext();) { EdgeImpl e = itr.next(); itr.remove(); @@ -942,6 +942,80 @@ public void testInOutIteratorRemove() { testContainsNone(edgeStore, Arrays.asList(edges)); } + @Test + public void testEdgeInOutMultiIterator() { + EdgeStore edgeStore = new EdgeStore(); + EdgeImpl[] edges = GraphGenerator.generateSmallEdgeList(); + edgeStore.addAll(Arrays.asList(edges)); + + // Get all nodes from the edges + List nodeList = Arrays.asList(getNodes(edges)); + + // Collect edges using multi-iterator + EdgeStore.EdgeInOutMultiIterator multiIterator = edgeStore.edgeIterator(nodeList.iterator(), true); + Set multiIteratorEdges = new ObjectOpenHashSet<>(); + while (multiIterator.hasNext()) { + EdgeImpl edge = multiIterator.next(); + multiIteratorEdges.add(edge); + } + + // Collect edges using individual iterators (old approach) + Set individualIteratorEdges = new ObjectOpenHashSet<>(); + for (NodeImpl node : nodeList) { + EdgeStore.EdgeInOutIterator singleIterator = edgeStore.edgeIterator(node, true); + while (singleIterator.hasNext()) { + EdgeImpl edge = singleIterator.next(); + individualIteratorEdges.add(edge); + } + } + + // Both approaches should yield the same set of edges + Assert.assertEquals(multiIteratorEdges, individualIteratorEdges); + + // Verify all edges are accounted for + Set allEdges = new ObjectOpenHashSet<>(Arrays.asList(edges)); + Assert.assertEquals(multiIteratorEdges, allEdges); + } + + @Test + public void testEdgeInOutMultiIteratorEmpty() { + EdgeStore edgeStore = new EdgeStore(); + List emptyNodeList = new ArrayList<>(); + + EdgeStore.EdgeInOutMultiIterator multiIterator = edgeStore.edgeIterator(emptyNodeList.iterator(), true); + Assert.assertFalse(multiIterator.hasNext()); + } + + @Test + public void testEdgeInOutMultiIteratorRemove() { + EdgeStore edgeStore = new EdgeStore(); + EdgeImpl[] edges = GraphGenerator.generateSmallEdgeList(); + edgeStore.addAll(Arrays.asList(edges)); + + List nodeList = Arrays.asList(getNodes(edges)); + EdgeStore.EdgeInOutMultiIterator multiIterator = edgeStore.edgeIterator(nodeList.iterator(), true); + + int initialSize = edgeStore.size(); + int removedCount = 0; + + while (multiIterator.hasNext()) { + EdgeImpl edge = multiIterator.next(); + multiIterator.remove(); + removedCount++; + Assert.assertFalse(edgeStore.contains(edge)); + Assert.assertEquals(edgeStore.size(), initialSize - removedCount); + } + + Assert.assertEquals(removedCount, initialSize); + Assert.assertTrue(edgeStore.isEmpty()); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testEdgeInOutMultiIteratorNullIterator() { + EdgeStore edgeStore = new EdgeStore(); + edgeStore.edgeIterator((Iterator) null, true); + } + @Test public void testOutTypeIterator() { EdgeImpl[] edges = GraphGenerator.generateSmallMultiTypeEdgeList(); @@ -1966,7 +2040,7 @@ public void testInOutUndirectedIteratorRemove() { edgeStore.addAll(Arrays.asList(edges)); for (NodeImpl n : getNodes(edges)) { - Iterator itr = edgeStore.edgeUndirectedIterator(n); + Iterator itr = edgeStore.edgeUndirectedIterator(n, true); for (; itr.hasNext();) { itr.next(); itr.remove(); @@ -1983,7 +2057,7 @@ public void testInOutUndirectedIteratorRemoveDecorator() { edgeStore.addAll(Arrays.asList(edges)); for (NodeImpl n : getNodes(edges)) { - Iterator itr = edgeStore.edgeUndirectedIterator(n); + Iterator itr = edgeStore.edgeUndirectedIterator(n, true); for (; itr.hasNext();) { itr.next(); itr.remove(); @@ -2000,7 +2074,7 @@ public void testInOutUndirectedIterator() { Object2ObjectMap> neighbours = getNeighboursMap(edges, 0, true); for (NodeImpl n : getNodes(edges)) { - Iterator itr = edgeStore.edgeUndirectedIterator(n); + Iterator itr = edgeStore.edgeUndirectedIterator(n, true); Set incidentEdges = neighbours.get(n); diff --git a/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/src/test/java/org/gephi/graph/impl/GraphGenerator.java index d9a351b0..31ddf79e 100644 --- a/src/test/java/org/gephi/graph/impl/GraphGenerator.java +++ b/src/test/java/org/gephi/graph/impl/GraphGenerator.java @@ -29,6 +29,7 @@ import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Node; +import org.gephi.graph.api.Rect2D; import org.gephi.graph.api.TimeRepresentation; import org.gephi.graph.impl.BasicGraphStore.BasicEdgeStore; @@ -387,9 +388,20 @@ public static NodeImpl[] generateNodeList(int nodeCount) { } public static NodeImpl[] generateNodeList(int nodeCount, GraphStore graphStore) { + return generateNodeList(nodeCount, graphStore, null); + } + + public static NodeImpl[] generateNodeList(int nodeCount, GraphStore graphStore, Rect2D area) { NodeImpl[] nodes = new NodeImpl[nodeCount]; + Random random = new Random(); for (int i = 0; i < nodeCount; i++) { NodeImpl node = new NodeImpl(String.valueOf(i), graphStore); + if (area != null) { + float x = area.minX + random.nextFloat() * (area.maxX - area.minX); + float y = area.minY + random.nextFloat() * (area.maxY - area.minY); + node.setPosition(x, y); + node.setSize(1.0f); + } nodes[i] = node; } return nodes; diff --git a/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java b/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java index 4ea74081..9f12091b 100644 --- a/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java +++ b/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java @@ -1,7 +1,29 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import java.util.Arrays; import java.util.Collection; import java.util.Random; +import java.util.stream.Collectors; +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.EdgeIterable; import org.gephi.graph.api.Node; import org.gephi.graph.api.NodeIterable; import org.gephi.graph.api.Rect2D; @@ -24,6 +46,14 @@ public void testGetAllNodesEmpty() { NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); Assert.assertTrue(q.getAllNodes().toCollection().isEmpty()); Assert.assertEquals(q.getAllNodes().toArray().length, 0); + Assert.assertEquals(q.getNodeCount(false), 1); + } + + @Test + public void testGetNodeCount() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + Assert.assertEquals(q.getNodeCount(false), 1); + Assert.assertEquals(q.getNodeCount(true), 0); } @Test @@ -37,6 +67,7 @@ public void testRemoveEmpty() { NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); NodeImpl node = new NodeImpl("0"); Assert.assertFalse(q.removeNode(node)); + Assert.assertEquals(q.getNodeCount(true), 0); } @Test @@ -45,6 +76,7 @@ public void testAddNode() { NodeImpl node = new NodeImpl("0"); Assert.assertTrue(q.addNode(node)); Assert.assertNotNull(node.getSpatialData().quadTreeNode); + Assert.assertEquals(q.getNodeCount(true), 1); } @Test @@ -64,6 +96,7 @@ public void testClear() { Assert.assertNull(node.getSpatialData().quadTreeNode); Assert.assertTrue(q.getAllNodes().toCollection().isEmpty()); Assert.assertFalse(q.removeNode(node)); + Assert.assertEquals(q.getNodeCount(true), 0); } @Test @@ -102,6 +135,8 @@ public void testDepth() { q.addNode(node); } Assert.assertTrue(q.getDepth() >= 1); + Assert.assertEquals(q.getNodeCount(true), 4); + Assert.assertEquals(q.getNodeCount(false), 5); } @Test @@ -125,7 +160,8 @@ public void testGetAll() { Collection rectContainingAll = q.getNodes(BOUNDS_RECT).toCollection(); Assert.assertEquals(rectContainingAll, all); - Collection bigRectContainingAll = q.getNodes(-BOUNDS * 2, -BOUNDS * 2, BOUNDS, BOUNDS).toCollection(); + Collection bigRectContainingAll = q.getNodes(new Rect2D(-BOUNDS * 2, -BOUNDS * 2, BOUNDS, BOUNDS)) + .toCollection(); Assert.assertEquals(bigRectContainingAll, all); } @@ -151,12 +187,14 @@ public void testOutOfBoundsStillWorks() { Collection all = q.getAllNodes().toCollection(); Assert.assertEquals(all.size(), 3); + all = q.getAllNodes().stream().collect(Collectors.toList()); + Assert.assertEquals(all.size(), 3); - assertEmpty(q.getNodes(80, 80, 89.99f, 89.99f)); + assertEmpty(q.getNodes(new Rect2D(80, 80, 89.99f, 89.99f))); - assertSame(q.getNodes(95, 95, 99, 99), n1); - assertSame(q.getNodes(0, 0, 101, 101), n1, n2); - assertSame(q.getNodes(4, 4, 91, 91), n1, n2); + assertSameSet(q.getNodes(new Rect2D(95, 95, 99, 99)), n1); + assertSameSet(q.getNodes(new Rect2D(0, 0, 101, 101)), n1, n2); + assertSameSet(q.getNodes(new Rect2D(4, 4, 91, 91)), n1, n2); } @Test @@ -179,11 +217,11 @@ public void testGetZone1() { q.addNode(n2); q.addNode(n3); - assertEmpty(q.getNodes(80, 80, 89.99f, 89.99f)); + assertEmpty(q.getNodes(new Rect2D(80, 80, 89.99f, 89.99f))); - assertSame(q.getNodes(95, 95, 99, 99), n1); - assertSame(q.getNodes(0, 0, 101, 101), n2, n1); - assertSame(q.getNodes(4, 4, 91, 91), n2, n1); + assertSameSet(q.getNodes(new Rect2D(95, 95, 99, 99)), n1); + assertSameSet(q.getNodes(new Rect2D(0, 0, 101, 101)), n2, n1); + assertSameSet(q.getNodes(new Rect2D(4, 4, 91, 91)), n2, n1); } @Test @@ -206,19 +244,11 @@ public void testGetZone2() { q.addNode(n2); q.addNode(n3); - assertEmpty(q.getNodes(80, 80, 89.99f, 89.99f)); - - assertSame(q.getNodes(95, 95, 99, 99), n1); - assertSame(q.getNodes(0, 0, 101, 101), n1, n2); - assertSame(q.getNodes(4, 4, 91, 91), n1, n2); - } + assertEmpty(q.getNodes(new Rect2D(80, 80, 89.99f, 89.99f))); - private void assertSame(NodeIterable iterable, Node... expected) { - Assert.assertEqualsNoOrder(iterable.toArray(), expected); - } - - private void assertEmpty(NodeIterable iterable) { - Assert.assertEquals(iterable.toCollection().size(), 0); + assertSameSet(q.getNodes(new Rect2D(95, 95, 99, 99)), n1); + assertSameSet(q.getNodes(new Rect2D(0, 0, 101, 101)), n1, n2); + assertSameSet(q.getNodes(new Rect2D(4, 4, 91, 91)), n1, n2); } @Test @@ -526,4 +556,285 @@ public void testGetBoundariesSingleNodeAtOrigin() { Assert.assertEquals(boundaries.maxX, 1f); Assert.assertEquals(boundaries.maxY, 1f); } + + @Test + public void testGetMaximumObjectsReached() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + addRandomNodes(q, GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE, 0); + Assert.assertEquals(q.getObjectCount(), GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE); + Assert.assertEquals(q.getNodeCount(true), 1); + Assert.assertEquals(q.getNodeCount(false), 1); + NodeImpl[] newNodes = addRandomNodes(q, 1, GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE); + Assert.assertEquals(q.getNodeCount(true), 4); + Assert.assertEquals(q.getObjectCount(), GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE + 1); + q.removeNode(newNodes[0]); + Assert.assertEquals(q.getObjectCount(), GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE); + } + + @Test + public void testCountsWithLargerDepth() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + int totalNodes = GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE * 10; + addRandomNodes(q, totalNodes, 0); + Assert.assertEquals(q.getObjectCount(), totalNodes); + Assert.assertTrue(q.getNodeCount(true) >= 10); + Assert.assertTrue(q.getNodeCount(false) >= 10); + } + + @Test + public void testIteratorWithLargerGraph() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + int totalNodes = 100000; + NodeImpl[] nodes = addRandomNodes(q, totalNodes, 0); + assertSameSet(q.getAllNodes(), nodes); + } + + @Test + public void testGetNodesInArea() { + Rect2D area = new Rect2D(-1000, -1000, 1000, 1000); + NodesQuadTree q = new NodesQuadTree(area); + int totalNodes = 30000; + NodeImpl[] nodes = addRandomNodes(q, totalNodes, 0, area); + Rect2D subarea = new Rect2D(-100, -100, 100, 100); + + assertSameSet(q + .getNodes(subarea, false), Arrays + .stream(nodes).filter(n -> subarea.intersects(n.getSpatialData().minX, n + .getSpatialData().minY, n.getSpatialData().maxX, n.getSpatialData().maxY)) + .toArray(NodeImpl[]::new)); + } + + @Test + public void testGetNodesInAreaApproximate() { + Rect2D area = new Rect2D(-1000, -1000, 1000, 1000); + NodesQuadTree q = new NodesQuadTree(area); + int totalNodes = 30000; + NodeImpl[] nodes = addRandomNodes(q, totalNodes, 0, area); + Rect2D subarea = new Rect2D(-100, -100, -1, -1); + + // Approximate should return all nodes that are in the quadtree nodes + // intersecting the area + assertSameSet(q.getNodes(subarea, true), Arrays.stream(nodes) + .filter(n -> subarea.intersects(n.getSpatialData().quadTreeNode.quadRect())).toArray(NodeImpl[]::new)); + } + + @Test + public void testGetNodesInAreaWithPredicate() { + NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); + + Rect2D rect = new Rect2D(-10, -10, 10, 10); + NodeImpl[] nodes = addRandomNodes(q, 2, 0, rect); + NodeImpl node1 = nodes[0]; + + assertSameSet(q.getNodes(rect, false, n -> n == node1), node1); + } + + @Test + public void testGetAllEdges() { + GraphStore store = GraphGenerator.generateEmptyGraphStore(getConfig()); + NodesQuadTree q = store.spatialIndex.nodesTree; + NodeImpl[] nodes = addRandomNodes(q, 2, 0); + EdgeImpl[] edges = addRandomEdges(store, nodes, 10); + + assertSameSet(q.getEdges(), edges); + } + + @Test + public void testGetAllEdgesLarge() { + GraphStore store = GraphGenerator.generateEmptyGraphStore(getConfig()); + NodesQuadTree q = store.spatialIndex.nodesTree; + NodeImpl[] nodes = addRandomNodes(q, 10000, 0); + EdgeImpl[] edges = addRandomEdges(store, nodes, 100000); + + assertSameSet(q.getEdges(), edges); + } + + @Test + public void testGetEdgesInArea() { + Rect2D area = new Rect2D(-1000, -1000, 1000, 1000); + GraphStore store = GraphGenerator.generateEmptyGraphStore(getConfig()); + NodesQuadTree q = store.spatialIndex.nodesTree; + NodeImpl[] nodes = addRandomNodes(q, 30000, 0, area); + EdgeImpl[] edges = addRandomEdges(store, nodes, 100000); + + Rect2D subarea = new Rect2D(-100, -100, 100, 100); + + assertSameSet(q.getEdges(subarea, false), Arrays.stream(edges) + .filter(e -> edgeIntersectsArea(e, subarea, false)).toArray(EdgeImpl[]::new)); + } + + @Test + public void testGetEdgesInAreaGlobal() { + Rect2D area = new Rect2D(-1000, -1000, 1000, 1000); + GraphStore store = GraphGenerator.generateEmptyGraphStore(getConfig()); + NodesQuadTree q = store.spatialIndex.nodesTree; + NodeImpl[] nodes = addRandomNodes(q, 30000, 0, area); + EdgeImpl[] edges = addRandomEdges(store, nodes, 100000); + + Rect2D subarea = new Rect2D(-600, -600, 600, 600); + + assertSameSet(q.getEdges(subarea, false), Arrays.stream(edges) + .filter(e -> edgeIntersectsArea(e, subarea, false)).toArray(EdgeImpl[]::new)); + } + + @Test + public void testGetEdgesInAreaApproximate() { + Rect2D area = new Rect2D(-1000, -1000, 1000, 1000); + GraphStore store = GraphGenerator.generateEmptyGraphStore(getConfig()); + NodesQuadTree q = store.spatialIndex.nodesTree; + NodeImpl[] nodes = addRandomNodes(q, 30000, 0, area); + EdgeImpl[] edges = addRandomEdges(store, nodes, 100000); + + Rect2D subarea = new Rect2D(-100, -100, -1, -1); + + assertSameSet(q.getEdges(subarea, true), Arrays.stream(edges).filter(e -> edgeIntersectsArea(e, subarea, true)) + .toArray(EdgeImpl[]::new)); + } + + @Test + public void testGetEdgesInAreaWithPredicate() { + GraphStore store = GraphGenerator.generateEmptyGraphStore(getConfig()); + NodesQuadTree q = store.spatialIndex.nodesTree; + + Rect2D rect = new Rect2D(-10, -10, 10, 10); + NodeImpl[] nodes = addRandomNodes(q, 2, 0, rect); + EdgeImpl[] edges = addRandomEdges(store, nodes, 2); + EdgeImpl edge1 = edges[0]; + + assertSameSet(q.getEdges(rect, false), edges); + assertSameSet(q.getEdges(rect, false, e -> e == edge1), edge1); + assertSameSet(q.getEdges(rect, true), edges); + assertSameSet(q.getEdges(rect, true, e -> e == edge1), edge1); + } + + @Test + public void testGetEdgesInAreaBidirectional() { + Rect2D rect = new Rect2D(100, 100, 100, 100); + GraphStore store = GraphGenerator.generateEmptyGraphStore(getConfig()); + NodeImpl[] nodes = GraphGenerator.generateNodeList(10000, store, rect); + store.addAllNodes(Arrays.asList(nodes)); + NodesQuadTree q = store.spatialIndex.nodesTree; + + nodes[0].setPosition(-1000, -1000); + EdgeImpl[] edges = addRandomEdges(store, new NodeImpl[] { nodes[0], nodes[1] }, 1); + + // Edge should be returned once, as only one node is in the area + assertSameSetAndCount(q.getEdges(new Rect2D(-2000, -2000, -999, -999), false), edges); + + nodes[1].setPosition(-1000, -1000); + + // Edge should be returned twice, once for each node + assertSameSetAndCount(q + .getEdges(new Rect2D(-2000, -2000, -999, -999), false), new EdgeImpl[] { edges[0], edges[0] }); + } + + // Utils + + private void assertSameSet(NodeIterable iterable, Node... expected) { + Assert.assertTrue(expected.length > 0, "Expected array must not be empty"); + ObjectSet set = new ObjectOpenHashSet<>(expected.length); + set.addAll(Arrays.asList(expected)); + Assert.assertEquals(iterable.toSet(), set); + Assert.assertEquals(iterable.stream().collect(Collectors.toSet()), set); + Assert.assertEquals(iterable.parallelStream().collect(Collectors.toSet()), set); + } + + private void assertSameSetAndCount(NodeIterable iterable, Node... expected) { + assertSameSet(iterable, expected); + Assert.assertEquals(iterable.toCollection().size(), expected.length); + Assert.assertEquals(iterable.stream().count(), expected.length); + Assert.assertEquals(iterable.parallelStream().count(), expected.length); + } + + private void assertSameSet(EdgeIterable iterable, Edge... expected) { + Assert.assertTrue(expected.length > 0, "Expected array must not be empty"); + ObjectSet set = new ObjectOpenHashSet<>(expected.length); + set.addAll(Arrays.asList(expected)); + Assert.assertEquals(iterable.toSet(), set); + Assert.assertEquals(iterable.stream().collect(Collectors.toSet()), set); + Assert.assertEquals(iterable.parallelStream().collect(Collectors.toSet()), set); + } + + private void assertSameSetAndCount(EdgeIterable iterable, Edge... expected) { + assertSameSet(iterable, expected); + Assert.assertEquals(iterable.toCollection().size(), expected.length); + Assert.assertEquals(iterable.stream().count(), expected.length); + Assert.assertEquals(iterable.parallelStream().count(), expected.length); + } + + private void assertEmpty(NodeIterable iterable) { + Assert.assertEquals(iterable.toCollection().size(), 0); + } + + private NodeImpl[] addRandomNodes(GraphStore store, int count, int startIndex) { + return addRandomNodes(store, count, startIndex, BOUNDS_RECT); + } + + private NodeImpl[] addRandomNodes(GraphStore store, int count, int startIndex, Rect2D area) { + NodeImpl[] nodes = generateNodes(count, startIndex, area); + for (NodeImpl n : nodes) { + store.addNode(n); + } + return nodes; + } + + private NodeImpl[] addRandomNodes(NodesQuadTree q, int count, int startIndex) { + return addRandomNodes(q, count, startIndex, BOUNDS_RECT); + } + + private NodeImpl[] addRandomNodes(NodesQuadTree q, int count, int startIndex, Rect2D area) { + NodeImpl[] nodes = generateNodes(count, startIndex, area); + for (NodeImpl n : nodes) { + q.addNode(n); + } + return nodes; + } + + private NodeImpl[] generateNodes(int count, int startIndex, Rect2D area) { + Random rand = new Random(); + NodeImpl[] nodes = new NodeImpl[count]; + for (int i = 0; i < count; i++) { + NodeImpl node = new NodeImpl(String.valueOf(startIndex++)); + float x = area.minX + rand.nextFloat() * (area.maxX - area.minX); + float y = area.minY + rand.nextFloat() * (area.maxY - area.minY); + node.setPosition(x, y); + node.setSize(1.0f); + nodes[i] = node; + } + return nodes; + } + + private EdgeImpl[] addRandomEdges(GraphStore store, NodeImpl[] nodes, int count) { + for (NodeImpl n : nodes) { + store.addNode(n); + } + EdgeImpl[] edges = new EdgeImpl[count]; + int edgeIndex = 0; + while (edgeIndex < count) { + NodeImpl source = nodes[new Random().nextInt(nodes.length)]; + NodeImpl target = nodes[new Random().nextInt(nodes.length)]; + if (source != target) { + EdgeImpl edge = new EdgeImpl(String.valueOf(edgeIndex), store, source, target, 0, 1.0, true); + edges[edgeIndex] = edge; + store.addEdge(edge); + edgeIndex++; + } + } + return edges; + } + + private Configuration getConfig() { + return Configuration.builder().enableSpatialIndex(true).build(); + } + + private boolean edgeIntersectsArea(EdgeImpl e, Rect2D area, boolean approximate) { + if (approximate) { + return area.intersects(e.source.getSpatialData().quadTreeNode.quadRect()) || area + .intersects(e.target.getSpatialData().quadTreeNode.quadRect()); + } + return area.intersects(e.source.getSpatialData().minX, e.source.getSpatialData().minY, e.source + .getSpatialData().maxX, e.source.getSpatialData().maxY) || area + .intersects(e.target.getSpatialData().minX, e.target.getSpatialData().minY, e.target + .getSpatialData().maxX, e.target.getSpatialData().maxY); + } } diff --git a/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java b/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java index 1868a6a5..8d775726 100644 --- a/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; import java.util.Arrays; @@ -37,7 +52,7 @@ public void testGetElementsBothNodesVisible() { SpatialIndexImpl spatialIndex = store.spatialIndex; assertSame(spatialIndex.getNodesInArea(BOUNDS_RECT), n1, n2); - assertSame(spatialIndex.getEdgesInArea(BOUNDS_RECT), e); + assertSame(spatialIndex.getEdgesInArea(BOUNDS_RECT), e, e); } @Test @@ -81,6 +96,19 @@ public void testGetElementsWithSelfLoop() { assertSame(spatialIndex.getEdgesInArea(BOUNDS_RECT), e); } + @Test + public void testClear() { + GraphStore store = GraphGenerator.generateTinyGraphStore(getConfig()); + + SpatialIndexImpl spatialIndex = store.spatialIndex; + Assert.assertEquals(spatialIndex.getObjectCount(), store.getNodeCount()); + Assert.assertEquals(spatialIndex.getNodesInArea(new Rect2D(-1, -1, 1, 1)).toCollection().size(), store + .getNodeCount()); + store.clear(); + Assert.assertEquals(spatialIndex.getObjectCount(), 0); + Assert.assertTrue(spatialIndex.getNodesInArea(new Rect2D(-1, -1, 1, 1)).toCollection().isEmpty()); + } + private void assertSame(NodeIterable iterable, Node... expected) { Assert.assertEquals(iterable.toCollection(), Arrays.asList(expected)); } From 959c0abf365acdd9fd486591da94f8e48f8b65e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 15:31:35 +0200 Subject: [PATCH 183/271] Bump org.sonatype.central:central-publishing-maven-plugin (#244) Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.7.0 to 0.9.0. - [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits) --- updated-dependencies: - dependency-name: org.sonatype.central:central-publishing-maven-plugin dependency-version: 0.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 350d84df..904bfdd9 100644 --- a/pom.xml +++ b/pom.xml @@ -105,7 +105,7 @@ org.sonatype.central central-publishing-maven-plugin - 0.7.0 + 0.9.0 true From e556429e7064bda03ae0d3062d1c91318532b692 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 15:36:13 +0200 Subject: [PATCH 184/271] Bump it.unimi.dsi:fastutil from 8.5.15 to 8.5.16 (#237) Bumps [it.unimi.dsi:fastutil](https://github.com/vigna/fastutil) from 8.5.15 to 8.5.16. - [Changelog](https://github.com/vigna/fastutil/blob/master/CHANGES) - [Commits](https://github.com/vigna/fastutil/compare/8.5.15...8.5.16) --- updated-dependencies: - dependency-name: it.unimi.dsi:fastutil dependency-version: 8.5.16 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 904bfdd9..e220b92b 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ it.unimi.dsi fastutil - 8.5.15 + 8.5.16 colt From bb61fbfd256cee548d47c7574c2be11162e0acb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 15:36:25 +0200 Subject: [PATCH 185/271] Bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.3 to 3.5.4 (#235) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.5.3 to 3.5.4. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.3...surefire-3.5.4) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e220b92b..c515c1a1 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.3 + 3.5.4 org.apache.maven.plugins From 9c4e00de5222d43cb9d1f9b9994c97914572de5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Oct 2025 15:36:35 +0200 Subject: [PATCH 186/271] Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.11.3 to 3.12.0 (#241) Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.3 to 3.12.0. - [Release notes](https://github.com/apache/maven-javadoc-plugin/releases) - [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.3...maven-javadoc-plugin-3.12.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-javadoc-plugin dependency-version: 3.12.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c515c1a1..279e1dea 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.11.3 + 3.12.0 org.apache.maven.plugins From 43ad13eefffe83b1210b8452678d04a847a9a979 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 3 Oct 2025 15:38:27 +0200 Subject: [PATCH 187/271] Upgrade formatter plugin --- pom.xml | 2 +- src/main/java/org/gephi/graph/api/types/package.html | 4 +--- src/main/java/org/gephi/graph/spi/package.html | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 279e1dea..c33be365 100644 --- a/pom.xml +++ b/pom.xml @@ -129,7 +129,7 @@ net.revelc.code.formatter formatter-maven-plugin - 2.23.0 + 2.29.0 org.codehaus.mojo diff --git a/src/main/java/org/gephi/graph/api/types/package.html b/src/main/java/org/gephi/graph/api/types/package.html index ca0a00dd..0a437254 100644 --- a/src/main/java/org/gephi/graph/api/types/package.html +++ b/src/main/java/org/gephi/graph/api/types/package.html @@ -1,6 +1,4 @@ - - Custom types the API supports, in addition of primitive and arrays. - + Custom types the API supports, in addition of primitive and arrays. diff --git a/src/main/java/org/gephi/graph/spi/package.html b/src/main/java/org/gephi/graph/spi/package.html index 39474ac9..78522c3c 100644 --- a/src/main/java/org/gephi/graph/spi/package.html +++ b/src/main/java/org/gephi/graph/spi/package.html @@ -1,6 +1,4 @@ - - SPI interfaces clients can implement to extend the API. - + SPI interfaces clients can implement to extend the API. From 20957aa3b7b2e64152d80802c47a326a6c39b874 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 3 Oct 2025 15:52:28 +0200 Subject: [PATCH 188/271] Update minimum version to Java 17 (#246) --- .github/workflows/ci.yml | 4 ++-- .github/workflows/pr.yml | 4 ++-- README.md | 2 +- pom.xml | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f16047b4..a138aa66 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: [ master, viz-engine ] + branches: [ master ] jobs: build: @@ -12,7 +12,7 @@ jobs: - name: Set up Maven Central Repository uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' server-id: central server-username: OSSRH_USER diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 8ecec0e6..b11ee6c7 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v5 with: - java-version: '11' + java-version: '17' distribution: 'temurin' - name: Build project with Maven run: mvn -B package --file pom.xml diff --git a/README.md b/README.md index 0a5e0aab..d784b779 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ compile 'org.gephi:graphstore:0.7.3' ## Dependencies -GraphStore is built for JRE 11+ and depends on FastUtil and Colt. +GraphStore is built for JRE 17+ and depends on FastUtil and Colt. For a complete list of dependencies, consult the `pom.xml` file. diff --git a/pom.xml b/pom.xml index c33be365..12380672 100644 --- a/pom.xml +++ b/pom.xml @@ -50,8 +50,8 @@ UTF-8 UTF-8 - 11 - 11 + 17 + 17 github @@ -293,7 +293,7 @@ true true none - 11 + 17 From a7583eeee8f51ff216ae80bfd1cfe2efd3c162e3 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 3 Oct 2025 16:26:24 +0200 Subject: [PATCH 189/271] Add predicate support in spatial index interface (#248) --- .../org/gephi/graph/api/SpatialIndex.java | 47 +++++++++++++++++++ .../gephi/graph/impl/GraphViewDecorator.java | 35 ++++++++++++++ .../gephi/graph/impl/SpatialIndexImpl.java | 4 ++ 3 files changed, 86 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java index 3502b669..a05eccaf 100644 --- a/src/main/java/org/gephi/graph/api/SpatialIndex.java +++ b/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -15,6 +15,8 @@ */ package org.gephi.graph.api; +import java.util.function.Predicate; + /** * Query the (quadtree-based) index based on the given rectangle area. *

@@ -42,6 +44,15 @@ public interface SpatialIndex { */ NodeIterable getNodesInArea(Rect2D rect); + /** + * Returns the nodes in the given area, filtered by the given predicate. + * + * @param rect area to query + * @param predicate filter predicate + * @return nodes in the area + */ + NodeIterable getNodesInArea(Rect2D rect, Predicate predicate); + /** * Returns the nodes in the given area using a faster, but approximate method. *

@@ -53,6 +64,19 @@ public interface SpatialIndex { */ NodeIterable getApproximateNodesInArea(Rect2D rect); + /** + * Returns the nodes in the given area using a faster, but approximate method, + * filtered by the given predicate. + *

+ * All nodes in the provided area are guaranteed to be returned, but some nodes + * outside the area may also be returned. + * + * @param rect area to query + * @param predicate filter predicate + * @return nodes in the area + */ + NodeIterable getApproximateNodesInArea(Rect2D rect, Predicate predicate); + /** * Returns the edges in the given area. Edges may be returned twice. * @@ -61,6 +85,16 @@ public interface SpatialIndex { */ EdgeIterable getEdgesInArea(Rect2D rect); + /** + * Returns the edges in the given area, filtered by the given predicate. Edges + * may be returned twice. + * + * @param rect area to query + * @param predicate filter predicate + * @return edges in the area + */ + EdgeIterable getEdgesInArea(Rect2D rect, Predicate predicate); + /** * Returns the edges in the given area using a faster, but approximate method. *

@@ -72,6 +106,19 @@ public interface SpatialIndex { */ EdgeIterable getApproximateEdgesInArea(Rect2D rect); + /** + * Returns the edges in the given area using a faster, but approximate method, + * filtered by the given predicate. + *

+ * All edges in the provided area are guaranteed to be returned, but some edges + * outside the area may also be returned. Edges may also be returned twice. + * + * @param rect area to query + * @param predicate filter predicate + * @return edges in the area + */ + EdgeIterable getApproximateEdgesInArea(Rect2D rect, Predicate predicate); + /** * Returns the bounding rectangle that contains all nodes in the graph. The * boundaries are calculated based on each node's position and size. diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 1a05db41..3a7b55e4 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -21,6 +21,7 @@ import java.util.Spliterator; import java.util.ConcurrentModificationException; import java.util.function.Consumer; +import java.util.function.Predicate; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.EdgeIterable; @@ -874,6 +875,14 @@ public NodeIterable getNodesInArea(Rect2D rect) { return graphStore.spatialIndex.getNodesInArea(rect, view::containsNode); } + @Override + public NodeIterable getNodesInArea(Rect2D rect, Predicate predicate) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return graphStore.spatialIndex.getNodesInArea(rect, (node) -> view.containsNode(node) && predicate.test(node)); + } + @Override public NodeIterable getApproximateNodesInArea(Rect2D rect) { if (graphStore.spatialIndex == null) { @@ -882,6 +891,15 @@ public NodeIterable getApproximateNodesInArea(Rect2D rect) { return graphStore.spatialIndex.getApproximateNodesInArea(rect, view::containsNode); } + @Override + public NodeIterable getApproximateNodesInArea(Rect2D rect, Predicate predicate) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return graphStore.spatialIndex + .getApproximateNodesInArea(rect, (node) -> view.containsNode(node) && predicate.test(node)); + } + @Override public EdgeIterable getEdgesInArea(Rect2D rect) { if (graphStore.spatialIndex == null) { @@ -890,6 +908,14 @@ public EdgeIterable getEdgesInArea(Rect2D rect) { return graphStore.spatialIndex.getEdgesInArea(rect, view::containsEdge); } + @Override + public EdgeIterable getEdgesInArea(Rect2D rect, Predicate predicate) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return graphStore.spatialIndex.getEdgesInArea(rect, (edge) -> view.containsEdge(edge) && predicate.test(edge)); + } + @Override public EdgeIterable getApproximateEdgesInArea(Rect2D rect) { if (graphStore.spatialIndex == null) { @@ -898,6 +924,15 @@ public EdgeIterable getApproximateEdgesInArea(Rect2D rect) { return graphStore.spatialIndex.getApproximateEdgesInArea(rect, view::containsEdge); } + @Override + public EdgeIterable getApproximateEdgesInArea(Rect2D rect, Predicate predicate) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return graphStore.spatialIndex + .getApproximateEdgesInArea(rect, (edge) -> view.containsEdge(edge) && predicate.test(edge)); + } + @Override public Rect2D getBoundaries() { if (graphStore.spatialIndex == null) { diff --git a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java index 7eebbff4..aac38710 100644 --- a/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/SpatialIndexImpl.java @@ -53,18 +53,22 @@ public void spatialIndexReadUnlock() { nodesTree.readUnlock(); } + @Override public NodeIterable getNodesInArea(Rect2D rect, Predicate predicate) { return nodesTree.getNodes(rect, false, predicate); } + @Override public NodeIterable getApproximateNodesInArea(Rect2D rect, Predicate predicate) { return nodesTree.getNodes(rect, true, predicate); } + @Override public EdgeIterable getEdgesInArea(Rect2D rect, Predicate predicate) { return nodesTree.getEdges(rect, false, predicate); } + @Override public EdgeIterable getApproximateEdgesInArea(Rect2D rect, Predicate predicate) { return nodesTree.getEdges(rect, true, predicate); } From 8afd5944ba5d34a5023c4aae0d2cf18ecaef3cc6 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 3 Oct 2025 18:19:25 +0200 Subject: [PATCH 190/271] Remove some superfluous checks --- .../org/gephi/graph/impl/GraphViewStore.java | 19 ++++++++++++++----- .../gephi/graph/impl/GraphViewStoreTest.java | 12 ++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 28b94e50..17e8e2d2 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -82,6 +82,7 @@ public GraphViewImpl createView(GraphView view, boolean nodes, boolean edges) { } } else { checkNonNullViewObject(view); + checkGraphViewObject(view); checkViewExist((GraphViewImpl) view); graphStore.autoWriteLock(); @@ -96,9 +97,13 @@ public GraphViewImpl createView(GraphView view, boolean nodes, boolean edges) { } public void destroyView(GraphView view) { + if (view.isMainView()) { + throw new IllegalArgumentException("Can't delete the main view"); + } graphStore.autoWriteLock(); try { checkNonNullViewObject(view); + checkGraphViewObject(view); TimeIndexStore nodeTimeStore = graphStore.timeStore.nodeIndexStore; if (nodeTimeStore != null) { @@ -160,6 +165,7 @@ public int size() { public Subgraph getGraph(GraphView view) { checkNonNullViewObject(view); + checkGraphViewObject(view); if (graphStore.isUndirected()) { if (view.isMainView()) { @@ -176,6 +182,7 @@ public Subgraph getGraph(GraphView view) { public DirectedSubgraph getDirectedGraph(GraphView view) { checkNonNullViewObject(view); + checkGraphViewObject(view); if (view.isMainView()) { return graphStore; @@ -187,6 +194,7 @@ public DirectedSubgraph getDirectedGraph(GraphView view) { public UndirectedSubgraph getUndirectedGraph(GraphView view) { checkNonNullViewObject(view); + checkGraphViewObject(view); if (view.isMainView()) { return graphStore.undirectedDecorator; @@ -358,14 +366,15 @@ public boolean deepEquals(GraphViewStore obj) { return true; } - protected void checkNonNullViewObject(final Object o) { + protected void checkNonNullViewObject(final GraphView o) { if (o == null) { throw new NullPointerException(); } - if (o != graphStore.mainGraphView) { - if (!(o instanceof GraphViewImpl)) { - throw new ClassCastException("View must be a GraphViewImpl object"); - } + } + + protected void checkGraphViewObject(final GraphView o) { + if (!o.isMainView() && !(o instanceof GraphViewImpl)) { + throw new IllegalArgumentException("The view is not from this implementation"); } } diff --git a/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java index 2efe9dba..cbc6531a 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java @@ -71,6 +71,14 @@ public void testDestroy() { Assert.assertTrue(view.isDestroyed()); } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testDestroyMainView() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + + store.destroyView(graphStore.mainGraphView); + } + @Test(expectedExceptions = IllegalArgumentException.class) public void testDestroyTwice() { GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); @@ -199,7 +207,7 @@ public void testGetUndirectedGraphNull() { store.getUndirectedGraph(null); } - @Test(expectedExceptions = ClassCastException.class) + @Test(expectedExceptions = IllegalArgumentException.class) public void testGetViewAnonymousClass() { GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); GraphViewStore store = graphStore.viewStore; @@ -212,7 +220,7 @@ public GraphModel getGraphModel() { @Override public boolean isMainView() { - throw new UnsupportedOperationException("Not supported yet."); + return false; } @Override From 0657b72eca0df9d6683d916b1738c3af4eb72add Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 9 Oct 2025 22:00:23 +0200 Subject: [PATCH 191/271] Column no-index gets it version incremented (#252) --- .../java/org/gephi/graph/api/ColumnIndex.java | 2 +- .../java/org/gephi/graph/impl/IndexStore.java | 10 +++---- .../org/gephi/graph/impl/GraphModelTest.java | 26 +++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/ColumnIndex.java b/src/main/java/org/gephi/graph/api/ColumnIndex.java index 32c28d99..41cf19f7 100644 --- a/src/main/java/org/gephi/graph/api/ColumnIndex.java +++ b/src/main/java/org/gephi/graph/api/ColumnIndex.java @@ -20,7 +20,7 @@ import java.util.Set; /** - * A column index is associated with a column and and keeps track of each unique + * A column index is associated with a column and keeps track of each unique * value and can also return the minimum and maximum values in case of a * sortable value type. * diff --git a/src/main/java/org/gephi/graph/impl/IndexStore.java b/src/main/java/org/gephi/graph/impl/IndexStore.java index a39c1b6e..f13cff09 100644 --- a/src/main/java/org/gephi/graph/impl/IndexStore.java +++ b/src/main/java/org/gephi/graph/impl/IndexStore.java @@ -139,7 +139,7 @@ public void clear(T element) { final ColumnImpl[] cols = columnStore.columns; for (int i = 0; i < length; i++) { Column c = cols[i]; - if (c != null && c.isIndexed()) { + if (c != null) { Object value = elementImpl.getAttribute(c); mainIndex.remove(c, value, element); if (!viewIndexes.isEmpty()) { @@ -171,7 +171,7 @@ public void index(T element) { final ColumnImpl[] cols = columnStore.columns; for (int i = 0; i < length; i++) { Column c = cols[i]; - if (c != null && c.isIndexed()) { + if (c != null) { Object value = elementImpl.getAttribute(c); value = mainIndex.put(c, value, element); elementImpl.attributes.setAttribute(c, value); @@ -202,7 +202,7 @@ public void indexView(Graph graph) { int length = columnStore.length; for (int i = 0; i < length; i++) { Column c = cols[i]; - if (c != null && c.isIndexed()) { + if (c != null) { Object value = element.getAttribute(c); viewIndex.put(c, value, element); } @@ -225,7 +225,7 @@ public void indexInView(T element, GraphView view) { final ColumnImpl[] cols = columnStore.columns; for (int i = 0; i < length; i++) { Column c = cols[i]; - if (c != null && c.isIndexed()) { + if (c != null) { Object value = elementImpl.getAttribute(c); index.put(c, value, element); } @@ -246,7 +246,7 @@ public void clearInView(T element, GraphView view) { final ColumnImpl[] cols = columnStore.columns; for (int i = 0; i < length; i++) { Column c = cols[i]; - if (c != null && c.isIndexed()) { + if (c != null) { Object value = elementImpl.getAttribute(c); index.remove(c, value, element); } diff --git a/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/src/test/java/org/gephi/graph/impl/GraphModelTest.java index 3fb68e4a..07125d01 100644 --- a/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -388,6 +388,32 @@ public void testGetEdgeIndex() { Assert.assertSame(graphModel.getElementIndex(table), index); } + @Test + public void testIndexVersionWithIndexedColumn() { + GraphModelImpl graphModel = new GraphModelImpl(); + Table table = graphModel.getNodeTable(); + Column col = table.addColumn("foo", String.class); + Index index = graphModel.getNodeIndex(); + int version = index.getColumnIndex(col).getVersion(); + Node n1 = graphModel.factory().newNode("1"); + n1.setAttribute(col, "bar"); + graphModel.getStore().addNode(n1); + Assert.assertTrue(index.getColumnIndex(col).getVersion() > version); + } + + @Test + public void testIndexVersionWithNoIndexedColumn() { + GraphModelImpl graphModel = new GraphModelImpl(); + Table table = graphModel.getNodeTable(); + Column col = table.addColumn("foo", "foo", Integer.class, Origin.DATA, null, false); + Index index = graphModel.getNodeIndex(); + int version = index.getColumnIndex(col).getVersion(); + Node n1 = graphModel.factory().newNode("1"); + n1.setAttribute(col, 42); + graphModel.getStore().addNode(n1); + Assert.assertTrue(index.getColumnIndex(col).getVersion() > version); + } + @Test public void testGetEdgeIndexWithIndexConfigDisabled() { GraphModelImpl graphModel = new GraphModelImpl(Configuration.builder().enableIndexEdges(false).build()); From f53533b40e28574eb2b956d4aade4a5fdd4de554 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 12 Oct 2025 21:50:47 +0200 Subject: [PATCH 192/271] Fix #253 ensure column size on IndexImpl (#254) --- src/main/java/org/gephi/graph/impl/IndexImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/gephi/graph/impl/IndexImpl.java b/src/main/java/org/gephi/graph/impl/IndexImpl.java index c0d81694..bf14e00c 100644 --- a/src/main/java/org/gephi/graph/impl/IndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IndexImpl.java @@ -217,6 +217,7 @@ protected void addAllColumns(ColumnImpl[] cols) { ensureColumnSize(cols.length); for (ColumnImpl col : cols) { ColumnIndexImpl index = createIndex(col); + ensureColumnSize(col.storeId); columns[col.storeId] = index; columnsCount++; } From ac0112106b78e2e4303585a70669675a20e2ca95 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 13 Oct 2025 21:38:09 +0200 Subject: [PATCH 193/271] Improve error handling for wrong types set to attributes --- .../org/gephi/graph/impl/ElementImpl.java | 29 +++++--- .../org/gephi/graph/impl/ElementImplTest.java | 74 +++++++++++++------ 2 files changed, 68 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index b47f66df..ea0295a8 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -269,7 +269,7 @@ private void setTimeAttribute(Column column, Object value, Object timeObject) { checkColumn(column); checkColumnDynamic(column); checkReadOnlyColumn(column); - checkType(column, value); + checkDynamicType(column, value); Object newValue = attributes.setAttribute(column, value, timeObject); updateIndex(column, null, newValue); @@ -558,7 +558,7 @@ void checkColumnDynamicAttribute(Column column) { } } - void checkType(Column column, Object value) { + void checkDynamicType(Column column, Object value) { if (value != null) { Class typeClass = column.getTypeClass(); if (TimestampMap.class.isAssignableFrom(typeClass)) { @@ -591,24 +591,31 @@ void checkType(Column column, Object value) { throw new IllegalArgumentException( "The object class does not match with the dynamic type (" + typeClass.getName() + ")"); } - } else if (List.class.isAssignableFrom(typeClass)) { + } + } + } + + void checkType(Column column, Object value) { + if (value != null) { + Class typeClass = column.getTypeClass(); + if (List.class.isAssignableFrom(typeClass)) { if (!(value instanceof List)) { - throw new IllegalArgumentException( - "The object class does not match with the list type (" + typeClass.getName() + ")"); + throw new IllegalArgumentException("The object class " + value.getClass() + .getName() + " does not match with the list type (" + typeClass.getName() + ")"); } } else if (Set.class.isAssignableFrom(typeClass)) { if (!(value instanceof Set)) { - throw new IllegalArgumentException( - "The object class does not match with the set type (" + typeClass.getName() + ")"); + throw new IllegalArgumentException("The object class " + value.getClass() + .getName() + " does not match with the set type (" + typeClass.getName() + ")"); } } else if (Map.class.isAssignableFrom(typeClass)) { if (!(value instanceof Map)) { - throw new IllegalArgumentException( - "The object class does not match with the map type (" + typeClass.getName() + ")"); + throw new IllegalArgumentException("The object class " + value.getClass() + .getName() + " does not match with the map type (" + typeClass.getName() + ")"); } } else if (!value.getClass().equals(typeClass)) { - throw new IllegalArgumentException( - "The object class does not match with the column type (" + typeClass.getName() + ")"); + throw new IllegalArgumentException("The object class " + value.getClass() + .getName() + " does not match with the column type (" + typeClass.getName() + ")"); } } } diff --git a/src/test/java/org/gephi/graph/impl/ElementImplTest.java b/src/test/java/org/gephi/graph/impl/ElementImplTest.java index 55bb1e9d..382c5eef 100644 --- a/src/test/java/org/gephi/graph/impl/ElementImplTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementImplTest.java @@ -1117,51 +1117,77 @@ public void testGetTimestampAttributeInView() { } @Test - public void testCheckType() { + public void testCheckDynamicType() { GraphStore store = new GraphStore(); NodeImpl node = new NodeImpl("0", store); - node.checkType(new ColumnImpl("0", TimestampIntegerMap.class, null, null, Origin.DATA, false, false), 1); - node.checkType(new ColumnImpl("0", TimestampDoubleMap.class, null, null, Origin.DATA, false, false), 1.0); - node.checkType(new ColumnImpl("0", TimestampFloatMap.class, null, null, Origin.DATA, false, false), 1f); - node.checkType(new ColumnImpl("0", TimestampByteMap.class, null, null, Origin.DATA, false, false), (byte) 1); - node.checkType(new ColumnImpl("0", TimestampShortMap.class, null, null, Origin.DATA, false, false), (short) 1); - node.checkType(new ColumnImpl("0", TimestampLongMap.class, null, null, Origin.DATA, false, false), 1l); - node.checkType(new ColumnImpl("0", TimestampCharMap.class, null, null, Origin.DATA, false, false), 'a'); - node.checkType(new ColumnImpl("0", TimestampBooleanMap.class, null, null, Origin.DATA, false, false), true); - node.checkType(new ColumnImpl("0", TimestampStringMap.class, null, null, Origin.DATA, false, false), "foo"); + node.checkDynamicType(new ColumnImpl("0", TimestampIntegerMap.class, null, null, Origin.DATA, false, false), 1); + node.checkDynamicType(new ColumnImpl("0", TimestampDoubleMap.class, null, null, Origin.DATA, false, + false), 1.0); + node.checkDynamicType(new ColumnImpl("0", TimestampFloatMap.class, null, null, Origin.DATA, false, false), 1f); + node.checkDynamicType(new ColumnImpl("0", TimestampByteMap.class, null, null, Origin.DATA, false, + false), (byte) 1); + node.checkDynamicType(new ColumnImpl("0", TimestampShortMap.class, null, null, Origin.DATA, false, + false), (short) 1); + node.checkDynamicType(new ColumnImpl("0", TimestampLongMap.class, null, null, Origin.DATA, false, false), 1l); + node.checkDynamicType(new ColumnImpl("0", TimestampCharMap.class, null, null, Origin.DATA, false, false), 'a'); + node.checkDynamicType(new ColumnImpl("0", TimestampBooleanMap.class, null, null, Origin.DATA, false, + false), true); + node.checkDynamicType(new ColumnImpl("0", TimestampStringMap.class, null, null, Origin.DATA, false, + false), "foo"); } @Test(expectedExceptions = RuntimeException.class) - public void testCheckTypeWithWrongIntervalConfiguration() { + public void testCheckDynamicTypeWithWrongIntervalConfiguration() { GraphStore store = new GraphStore(); NodeImpl node = new NodeImpl("0", store); - node.checkType(new ColumnImpl("0", IntervalIntegerMap.class, null, null, Origin.DATA, false, false), 1); + node.checkDynamicType(new ColumnImpl("0", IntervalIntegerMap.class, null, null, Origin.DATA, false, false), 1); } @Test(expectedExceptions = RuntimeException.class) - public void testCheckTypeWithWrongTimestampConfiguration() { + public void testCheckDynamicTypeWithWrongTimestampConfiguration() { GraphStore store = getIntervalGraphStore(); NodeImpl node = new NodeImpl("0", store); - node.checkType(new ColumnImpl("0", TimestampIntegerMap.class, null, null, Origin.DATA, false, false), 1); + node.checkDynamicType(new ColumnImpl("0", TimestampIntegerMap.class, null, null, Origin.DATA, false, false), 1); } @Test - public void testCheckTypeInterval() { + public void testDynamicCheckTypeInterval() { GraphStore store = getIntervalGraphStore(); NodeImpl node = new NodeImpl("0", store); - node.checkType(new ColumnImpl("0", IntervalIntegerMap.class, null, null, Origin.DATA, false, false), 1); - node.checkType(new ColumnImpl("0", IntervalDoubleMap.class, null, null, Origin.DATA, false, false), 1.0); - node.checkType(new ColumnImpl("0", IntervalFloatMap.class, null, null, Origin.DATA, false, false), 1f); - node.checkType(new ColumnImpl("0", IntervalByteMap.class, null, null, Origin.DATA, false, false), (byte) 1); - node.checkType(new ColumnImpl("0", IntervalShortMap.class, null, null, Origin.DATA, false, false), (short) 1); - node.checkType(new ColumnImpl("0", IntervalLongMap.class, null, null, Origin.DATA, false, false), 1l); - node.checkType(new ColumnImpl("0", IntervalCharMap.class, null, null, Origin.DATA, false, false), 'a'); - node.checkType(new ColumnImpl("0", IntervalBooleanMap.class, null, null, Origin.DATA, false, false), true); - node.checkType(new ColumnImpl("0", IntervalStringMap.class, null, null, Origin.DATA, false, false), "foo"); + node.checkDynamicType(new ColumnImpl("0", IntervalIntegerMap.class, null, null, Origin.DATA, false, false), 1); + node.checkDynamicType(new ColumnImpl("0", IntervalDoubleMap.class, null, null, Origin.DATA, false, false), 1.0); + node.checkDynamicType(new ColumnImpl("0", IntervalFloatMap.class, null, null, Origin.DATA, false, false), 1f); + node.checkDynamicType(new ColumnImpl("0", IntervalByteMap.class, null, null, Origin.DATA, false, + false), (byte) 1); + node.checkDynamicType(new ColumnImpl("0", IntervalShortMap.class, null, null, Origin.DATA, false, + false), (short) 1); + node.checkDynamicType(new ColumnImpl("0", IntervalLongMap.class, null, null, Origin.DATA, false, false), 1l); + node.checkDynamicType(new ColumnImpl("0", IntervalCharMap.class, null, null, Origin.DATA, false, false), 'a'); + node.checkDynamicType(new ColumnImpl("0", IntervalBooleanMap.class, null, null, Origin.DATA, false, + false), true); + node.checkDynamicType(new ColumnImpl("0", IntervalStringMap.class, null, null, Origin.DATA, false, + false), "foo"); + } + + @Test + public void checkType() { + GraphStore store = new GraphStore(); + + NodeImpl node = new NodeImpl("0", store); + node.checkType(new ColumnImpl("0", Integer.class, null, null, Origin.DATA, false, false), 1); + node.checkType(new ColumnImpl("0", Double.class, null, null, Origin.DATA, false, false), 1.0); + node.checkType(new ColumnImpl("0", Float.class, null, null, Origin.DATA, false, false), 1f); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void checkTypeException() { + GraphStore store = new GraphStore(); + NodeImpl node = new NodeImpl("0", store); + node.checkType(new ColumnImpl("0", Integer.class, null, null, Origin.DATA, false, false), "foo"); } @Test From ffa68fcbcd0242dd431e8bf2c20ba1d91d2b50df Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 13 Oct 2025 21:38:21 +0200 Subject: [PATCH 194/271] Add unit test --- .../java/org/gephi/graph/impl/GraphBridgeTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java index 1bc1da3c..3a838d7b 100644 --- a/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphBridgeTest.java @@ -411,4 +411,18 @@ public void testCopyOtherTimesetColumn() { set.add(43.0); Assert.assertNotEquals(n1Copy.getAttribute(c1.getId()), set); } + + @Test + public void testCopyOnNonEmpty() { + GraphStore dest = GraphGenerator.generateTinyGraphStore(); + + GraphStore source = GraphGenerator.generateEmptyGraphStore(); + Node n1 = source.getModel().factory().newNode("foo"); + source.addNode(n1); + + new GraphBridgeImpl(dest).copyNodes(source.getNodes().toArray()); + Assert.assertNotNull(dest.getNode("foo")); + Assert.assertEquals(dest.getNodeCount(), 3); + Assert.assertEquals(dest.getEdgeCount(), 1); + } } From 7c10742b90f0921b0a304f9428ce40cc9708c234 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 13 Oct 2025 21:40:50 +0200 Subject: [PATCH 195/271] Set version to 0.8.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 12380672..d300e1d9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.0-SNAPSHOT + 0.8.0 jar GraphStore From d5aa5efb24bffe970275da23127c8c51dbb12376 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 13 Oct 2025 21:48:03 +0200 Subject: [PATCH 196/271] Set version to 0.8.1-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d300e1d9..462e27ad 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.0 + 0.8.1-SNAPSHOT jar GraphStore From 66b69ae709d330743287e6e694f45f4b6ab909be Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 13 Oct 2025 21:53:12 +0200 Subject: [PATCH 197/271] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d784b779..5276ba33 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.7.3 + 0.8.0 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.7.3' +compile 'org.gephi:graphstore:0.8.0' ``` ## Dependencies From c59e86961afa7229982442d784933fe807632fb3 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Oct 2025 16:48:12 +0100 Subject: [PATCH 198/271] Replace BitVector by BitSet and remove legacy library (#258) * Replace BitVector by BitSet and remove legacy library * Performance optimisation with bitset operations * Update README * Add some defensive checks when elements are removed --- README.md | 2 +- pom.xml | 5 - .../gephi/graph/impl/ColumnObserverImpl.java | 21 +- .../org/gephi/graph/impl/GraphViewImpl.java | 487 ++++++++++--- .../org/gephi/graph/impl/Serialization.java | 45 +- .../graph/impl/GraphViewDecoratorTest.java | 1 + .../gephi/graph/impl/GraphViewImplTest.java | 672 ++++++++++++++++++ .../gephi/graph/impl/SerializationTest.java | 32 +- 8 files changed, 1110 insertions(+), 155 deletions(-) diff --git a/README.md b/README.md index 5276ba33..5652e64b 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ compile 'org.gephi:graphstore:0.8.0' ## Dependencies -GraphStore is built for JRE 17+ and depends on FastUtil and Colt. +GraphStore is built for JRE 17+ and depends on FastUtil. For a complete list of dependencies, consult the `pom.xml` file. diff --git a/pom.xml b/pom.xml index 462e27ad..14e2c0da 100644 --- a/pom.xml +++ b/pom.xml @@ -67,11 +67,6 @@ fastutil 8.5.16 - - colt - colt - 1.2.0 - diff --git a/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java b/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java index d90aea76..f6a38ef4 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnObserverImpl.java @@ -15,8 +15,7 @@ */ package org.gephi.graph.impl; -import cern.colt.bitvector.BitVector; -import cern.colt.bitvector.QuickBitVector; +import java.util.BitSet; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectList; import it.unimi.dsi.fastutil.objects.ObjectLists; @@ -40,7 +39,7 @@ public class ColumnObserverImpl implements ColumnObserver { protected boolean destroyed; // Config protected final boolean withDiff; - protected BitVector bitVector; + protected BitSet bitVector; // Cache protected ColumnDiffImpl columnDiff; @@ -109,7 +108,7 @@ private void refreshDiff() { boolean node = AttributeUtils.isNodeColumn(column); columnDiff = node ? new NodeColumnDiffImpl() : new EdgeColumnDiffImpl(); - int size = bitVector.size(); + int size = bitVector.length(); for (int i = 0; i < size; i++) { boolean t = bitVector.get(i); @@ -179,19 +178,11 @@ public EdgeIterable getTouchedElements() { private void ensureVectorSize(ElementImpl element) { int sid = element.getStoreId(); if (bitVector == null) { - bitVector = new BitVector(sid + 1); - } else if (sid >= bitVector.size()) { - int newSize = Math.min(Math + int initialSize = Math.min(Math .max(sid + 1, (int) (sid * GraphStoreConfiguration.COLUMNDIFF_GROWING_FACTOR)), Integer.MAX_VALUE); - bitVector = growBitVector(bitVector, newSize); + bitVector = new BitSet(initialSize); } - } - - private BitVector growBitVector(BitVector bitVector, int size) { - long[] elements = bitVector.elements(); - long[] newElements = QuickBitVector.makeBitVector(size, 1); - System.arraycopy(elements, 0, newElements, 0, elements.length); - return new BitVector(newElements, size); + // BitSet grows automatically when setting bits, no need to manually grow } private void readLock() { diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index 6842d67a..ae02fb75 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -15,11 +15,9 @@ */ package org.gephi.graph.impl; -import cern.colt.bitvector.BitVector; -import cern.colt.bitvector.QuickBitVector; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -40,8 +38,8 @@ public class GraphViewImpl implements GraphView { protected final boolean nodeView; protected final boolean edgeView; protected final GraphAttributesImpl attributes; - protected BitVector nodeBitVector; - protected BitVector edgeBitVector; + protected BitSet nodeBitVector; + protected BitSet edgeBitVector; protected int storeId; // Version protected final GraphVersion version; @@ -64,11 +62,11 @@ public GraphViewImpl(final GraphStore store, boolean nodes, boolean edges) { this.edgeView = edges; this.attributes = new GraphAttributesImpl(); if (nodes) { - this.nodeBitVector = new BitVector(store.nodeStore.maxStoreId()); + this.nodeBitVector = new BitSet(store.nodeStore.maxStoreId()); } else { this.nodeBitVector = null; } - this.edgeBitVector = new BitVector(store.edgeStore.maxStoreId()); + this.edgeBitVector = new BitSet(store.edgeStore.maxStoreId()); this.typeCounts = new int[GraphStoreConfiguration.VIEW_DEFAULT_TYPE_COUNT]; this.mutualEdgeTypeCounts = new int[GraphStoreConfiguration.VIEW_DEFAULT_TYPE_COUNT]; @@ -85,17 +83,18 @@ public GraphViewImpl(final GraphViewImpl view, boolean nodes, boolean edges) { this.edgeView = edges; this.attributes = new GraphAttributesImpl(); if (nodes) { - this.nodeBitVector = view.nodeBitVector.copy(); + this.nodeBitVector = (BitSet) view.nodeBitVector.clone(); this.nodeCount = view.nodeCount; } else { this.nodeBitVector = null; } this.edgeCount = view.edgeCount; - this.edgeBitVector = view.edgeBitVector.copy(); + this.edgeBitVector = (BitSet) view.edgeBitVector.clone(); this.typeCounts = new int[view.typeCounts.length]; System.arraycopy(view.typeCounts, 0, typeCounts, 0, view.typeCounts.length); this.mutualEdgeTypeCounts = new int[view.mutualEdgeTypeCounts.length]; System.arraycopy(view.mutualEdgeTypeCounts, 0, mutualEdgeTypeCounts, 0, view.mutualEdgeTypeCounts.length); + this.mutualEdgesCount = view.mutualEdgesCount; this.directedDecorator = new GraphViewDecorator(graphStore, this, false); this.undirectedDecorator = new GraphViewDecorator(graphStore, this, true); this.version = graphStore.version != null ? new GraphVersion(directedDecorator) : null; @@ -144,9 +143,6 @@ public boolean addNode(final Node node) { int edgeid = edge.storeId; boolean edgeisSet = edgeBitVector.get(edgeid); if (!edgeisSet) { - - incrementEdgeVersion(); - addEdge(edge); } // End @@ -268,23 +264,25 @@ public boolean removeNodeAll(final Collection nodes) { public boolean retainNodes(final Collection c) { if (nodeView) { if (!c.isEmpty()) { - IntOpenHashSet set = new IntOpenHashSet(c.size()); + // Build BitSet of nodes to retain + BitSet retainSet = new BitSet(graphStore.nodeStore.maxStoreId()); for (Node o : c) { checkValidNodeObject(o); - set.add(o.getStoreId()); + retainSet.set(o.getStoreId()); } - boolean changed = false; - int nodeSize = nodeBitVector.size(); - for (int i = 0; i < nodeSize; i++) { - boolean t = nodeBitVector.get(i); - if (t && !set.contains(i)) { - if (removeNode(getNode(i))) { - changed = true; - } - } + // Find nodes to remove: nodes in this view but NOT in retain set + // This is equivalent to: nodeBitVector AND NOT retainSet + BitSet nodesToRemove = (BitSet) nodeBitVector.clone(); + nodesToRemove.andNot(retainSet); + + if (nodesToRemove.isEmpty()) { + return false; } - return changed; + + // Bulk remove nodes + bulkRemoveNodes(nodesToRemove); + return true; } else if (nodeCount != 0) { clear(); return true; @@ -296,22 +294,25 @@ public boolean retainNodes(final Collection c) { public boolean retainEdges(final Collection c) { if (edgeView) { if (!c.isEmpty()) { - IntOpenHashSet set = new IntOpenHashSet(c.size()); + // Build BitSet of edges to retain + BitSet retainSet = new BitSet(graphStore.edgeStore.maxStoreId()); for (Edge o : c) { checkValidEdgeObject(o); - set.add(o.getStoreId()); + retainSet.set(o.getStoreId()); } - boolean changed = false; - int edgeSize = edgeBitVector.size(); - for (int i = 0; i < edgeSize; i++) { - boolean t = edgeBitVector.get(i); - if (t && !set.contains(i)) { - removeEdge(getEdge(i)); - changed = true; - } + // Find edges to remove: edges in this view but NOT in retain set + // This is equivalent to: edgeBitVector AND NOT retainSet + BitSet edgesToRemove = (BitSet) edgeBitVector.clone(); + edgesToRemove.andNot(retainSet); + + if (edgesToRemove.isEmpty()) { + return false; } - return changed; + + // Bulk remove edges + bulkRemoveEdges(edgesToRemove); + return true; } else if (edgeCount != 0) { clearEdges(); return true; @@ -413,16 +414,10 @@ public void clearEdges() { public void fill() { if (nodeView) { - if (nodeCount > 0) { - nodeBitVector = new BitVector(graphStore.nodeStore.maxStoreId()); - } - nodeBitVector.not(); + nodeBitVector.set(0, graphStore.nodeStore.maxStoreId(), true); this.nodeCount = graphStore.nodeStore.size(); } - if (edgeCount > 0) { - edgeBitVector = new BitVector(graphStore.edgeStore.maxStoreId()); - } - edgeBitVector.not(); + edgeBitVector.set(0, graphStore.edgeStore.maxStoreId()); this.edgeCount = graphStore.edgeStore.size(); int typeLength = graphStore.edgeStore.longDictionary.length; @@ -474,90 +469,126 @@ public boolean containsEdge(final Edge edge) { } public void intersection(final GraphViewImpl otherView) { - BitVector nodeOtherBitVector = otherView.nodeBitVector; - BitVector edgeOtherBitVector = otherView.edgeBitVector; + BitSet nodeOtherBitVector = otherView.nodeBitVector; + BitSet edgeOtherBitVector = otherView.edgeBitVector; if (nodeView) { - int nodeSize = nodeBitVector.size(); - for (int i = 0; i < nodeSize; i++) { - boolean t = nodeBitVector.get(i); - boolean o = nodeOtherBitVector.get(i); - if (t && !o) { - removeNode(getNode(i)); - } + // Find nodes to remove: nodes in this view but NOT in other view + BitSet nodesToRemove = (BitSet) nodeBitVector.clone(); + nodesToRemove.andNot(nodeOtherBitVector); + + if (!nodesToRemove.isEmpty()) { + // Bulk remove nodes + bulkRemoveNodes(nodesToRemove); } } if (edgeView) { - int edgeSize = edgeBitVector.size(); - for (int i = 0; i < edgeSize; i++) { - boolean t = edgeBitVector.get(i); - boolean o = edgeOtherBitVector.get(i); - if (t && !o) { - removeEdge(getEdge(i)); - } + // Find edges to remove: edges in this view but NOT in other view + BitSet edgesToRemove = (BitSet) edgeBitVector.clone(); + edgesToRemove.andNot(edgeOtherBitVector); + + if (!edgesToRemove.isEmpty()) { + // Bulk remove edges + bulkRemoveEdges(edgesToRemove); } } } public void union(final GraphViewImpl otherView) { - BitVector nodeOtherBitVector = otherView.nodeBitVector; - BitVector edgeOtherBitVector = otherView.edgeBitVector; + BitSet nodeOtherBitVector = otherView.nodeBitVector; + BitSet edgeOtherBitVector = otherView.edgeBitVector; if (nodeView) { - int nodeSize = nodeBitVector.size(); - for (int i = 0; i < nodeSize; i++) { - boolean t = nodeBitVector.get(i); - boolean o = nodeOtherBitVector.get(i); - if (!t && o) { - addNode(getNode(i)); - } + // Find nodes to add: nodes in other view but NOT in this view + BitSet nodesToAdd = (BitSet) nodeOtherBitVector.clone(); + nodesToAdd.andNot(nodeBitVector); + + if (!nodesToAdd.isEmpty()) { + // Bulk add nodes + bulkAddNodes(nodesToAdd); } } if (edgeView) { - int edgeSize = edgeBitVector.size(); - for (int i = 0; i < edgeSize; i++) { - boolean t = edgeBitVector.get(i); - boolean o = edgeOtherBitVector.get(i); - if (!t && o) { - addEdge(getEdge(i)); - } + // Find edges to add: edges in other view but NOT in this view + BitSet edgesToAdd = (BitSet) edgeOtherBitVector.clone(); + edgesToAdd.andNot(edgeBitVector); + + if (!edgesToAdd.isEmpty()) { + // Bulk add edges + bulkAddEdges(edgesToAdd); } } } public void not() { + // Flip node bits if this is a node view if (nodeView) { - nodeBitVector.not(); + nodeBitVector.flip(0, graphStore.nodeStore.maxStoreId()); this.nodeCount = graphStore.nodeStore.size() - this.nodeCount; + incrementNodeVersion(); } - edgeBitVector.not(); + // Flip edge bits + edgeBitVector.flip(0, graphStore.edgeStore.maxStoreId()); + + // Update edge counts by subtracting from totals this.edgeCount = graphStore.edgeStore.size() - this.edgeCount; - for (int i = 0; i < typeCounts.length; i++) { + + // Ensure type count arrays are sized to match the store + int storeTypeLength = graphStore.edgeStore.longDictionary.length; + if (typeCounts.length < storeTypeLength) { + int[] newTypeCounts = new int[storeTypeLength]; + System.arraycopy(typeCounts, 0, newTypeCounts, 0, typeCounts.length); + typeCounts = newTypeCounts; + + int[] newMutualCounts = new int[storeTypeLength]; + System.arraycopy(mutualEdgeTypeCounts, 0, newMutualCounts, 0, mutualEdgeTypeCounts.length); + mutualEdgeTypeCounts = newMutualCounts; + } + + // Invert all type counts (including types that weren't in the view before) + for (int i = 0; i < storeTypeLength; i++) { this.typeCounts[i] = graphStore.edgeStore.longDictionary[i].size() - this.typeCounts[i]; } - for (int i = 0; i < mutualEdgeTypeCounts.length; i++) { + for (int i = 0; i < graphStore.edgeStore.mutualEdgesTypeSize.length; i++) { this.mutualEdgeTypeCounts[i] = graphStore.edgeStore.mutualEdgesTypeSize[i] - this.mutualEdgeTypeCounts[i]; } this.mutualEdgesCount = graphStore.edgeStore.mutualEdgesSize - this.mutualEdgesCount; - if (nodeView) { - incrementNodeVersion(); - } incrementEdgeVersion(); + // If node view is enabled, remove edges with invalid endpoints + // Optimization: Only iterate through edges that are NOW in the view (after + // flip) + // instead of all edges in the store if (nodeView) { - for (Edge e : graphStore.edgeStore) { - boolean t = edgeBitVector.get(e.getStoreId()); - if (t && (!nodeBitVector.get(e.getSource().getStoreId()) || !nodeBitVector - .get(e.getTarget().getStoreId()))) { - removeEdge((EdgeImpl) e); + BitSet edgesToRemove = new BitSet(); + + // Iterate only over edges that are set in the view (much faster for sparse + // views) + for (int edgeId = edgeBitVector.nextSetBit(0); edgeId >= 0; edgeId = edgeBitVector.nextSetBit(edgeId + 1)) { + EdgeImpl edge = getEdge(edgeId); + if (edge == null) { + // SAFETY: Edge no longer exists in store + edgesToRemove.set(edgeId); + continue; } + // Check if both endpoints are in the node view + if (!nodeBitVector.get(edge.source.storeId) || !nodeBitVector.get(edge.target.storeId)) { + edgesToRemove.set(edgeId); + } + } + + // Bulk remove invalid edges + if (!edgesToRemove.isEmpty()) { + bulkRemoveEdgesForNot(edgesToRemove); } } + // Rebuild indexes (necessary for NOT operation as the view content has + // completely changed) if (nodeView) { IndexStore nodeIndexStore = graphStore.nodeTable.store.indexStore; if (nodeIndexStore != null) { @@ -590,6 +621,251 @@ public void addEdgeInNodeView(EdgeImpl edge) { } } + /** + * Bulk remove nodes from the view. This is more efficient than removing nodes + * one by one as it batches index updates and increments version only once. + */ + private void bulkRemoveNodes(BitSet nodesToRemove) { + // First pass: collect all edges to remove (incident to removed nodes) + BitSet edgesToRemove = new BitSet(); + for (int nodeId = nodesToRemove.nextSetBit(0); nodeId >= 0; nodeId = nodesToRemove.nextSetBit(nodeId + 1)) { + NodeImpl node = getNode(nodeId); + if (node == null) { + continue; // SAFETY: Node was removed from store (storeId reused or deleted) + } + + EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node, false); + while (itr.hasNext()) { + EdgeImpl edge = itr.next(); + int edgeId = edge.storeId; + if (edgeBitVector.get(edgeId)) { + edgesToRemove.set(edgeId); + } + } + } + + // Remove edges in bulk + if (!edgesToRemove.isEmpty()) { + bulkRemoveEdges(edgesToRemove); + } + + // Update node bit vector + int removedCount = nodesToRemove.cardinality(); + nodeBitVector.andNot(nodesToRemove); + nodeCount -= removedCount; + incrementNodeVersion(); + + // Bulk update indexes + IndexStore indexStore = graphStore.nodeTable.store.indexStore; + TimeIndexStore timeIndexStore = graphStore.timeStore.nodeIndexStore; + + if (indexStore != null || timeIndexStore != null) { + for (int i = nodesToRemove.nextSetBit(0); i >= 0; i = nodesToRemove.nextSetBit(i + 1)) { + NodeImpl node = getNode(i); + if (node != null) { // SAFETY: Skip if node no longer exists + if (indexStore != null) { + indexStore.clearInView(node, this); + } + if (timeIndexStore != null) { + timeIndexStore.clearInView(node, this); + } + } + } + } + } + + /** + * Bulk add nodes to the view. This is more efficient than adding nodes one by + * one as it batches index updates and increments version only once. + */ + private void bulkAddNodes(BitSet nodesToAdd) { + // Update node bit vector + int addedCount = nodesToAdd.cardinality(); + nodeBitVector.or(nodesToAdd); + nodeCount += addedCount; + incrementNodeVersion(); + + // Bulk update indexes + IndexStore indexStore = graphStore.nodeTable.store.indexStore; + TimeIndexStore timeIndexStore = graphStore.timeStore.nodeIndexStore; + + if (indexStore != null || timeIndexStore != null) { + for (int i = nodesToAdd.nextSetBit(0); i >= 0; i = nodesToAdd.nextSetBit(i + 1)) { + NodeImpl node = getNode(i); + if (node != null) { // SAFETY: Skip if node no longer exists + if (indexStore != null) { + indexStore.indexInView(node, this); + } + if (timeIndexStore != null) { + timeIndexStore.indexInView(node, this); + } + } + } + } + + // If nodeView && !edgeView, add edges between newly added nodes and existing + // nodes + if (nodeView && !edgeView) { + BitSet edgesToAdd = new BitSet(); + for (int nodeId = nodesToAdd.nextSetBit(0); nodeId >= 0; nodeId = nodesToAdd.nextSetBit(nodeId + 1)) { + NodeImpl node = getNode(nodeId); + if (node == null) { + continue; // SAFETY: Skip if node no longer exists + } + + EdgeInOutIterator itr = graphStore.edgeStore.edgeIterator(node, false); + while (itr.hasNext()) { + EdgeImpl edge = itr.next(); + NodeImpl opposite = edge.source == node ? edge.target : edge.source; + // Check if opposite node is in view and edge is not already in view + if (nodeBitVector.get(opposite.storeId) && !edgeBitVector.get(edge.storeId)) { + edgesToAdd.set(edge.storeId); + } + } + } + + if (!edgesToAdd.isEmpty()) { + bulkAddEdges(edgesToAdd); + } + } + } + + /** + * Bulk remove edges from the view. This is more efficient than removing edges + * one by one as it updates stats in bulk and increments version only once. + */ + private void bulkRemoveEdges(BitSet edgesToRemove) { + // Update edge bit vector + int removedCount = edgesToRemove.cardinality(); + edgeBitVector.andNot(edgesToRemove); + edgeCount -= removedCount; + + // Update type counts and mutual edge counts + for (int i = edgesToRemove.nextSetBit(0); i >= 0; i = edgesToRemove.nextSetBit(i + 1)) { + EdgeImpl edge = getEdge(i); + if (edge == null) { + continue; // SAFETY: Edge was removed from store (storeId reused or deleted) + } + + int type = edge.type; + ensureTypeCountArrayCapacity(type); + typeCounts[type]--; + + if (edge.isMutual() && !edge.isSelfLoop()) { + EdgeImpl reverseEdge = graphStore.edgeStore.get(edge.target, edge.source, edge.type, false); + if (reverseEdge != null && containsEdge(reverseEdge)) { + mutualEdgeTypeCounts[type]--; + mutualEdgesCount--; + } + } + } + + incrementEdgeVersion(); + + // Bulk update indexes + IndexStore indexStore = graphStore.edgeTable.store.indexStore; + TimeIndexStore timeIndexStore = graphStore.timeStore.edgeIndexStore; + + if (indexStore != null || timeIndexStore != null) { + for (int i = edgesToRemove.nextSetBit(0); i >= 0; i = edgesToRemove.nextSetBit(i + 1)) { + EdgeImpl edge = getEdge(i); + if (edge != null) { // SAFETY: Skip if edge no longer exists + if (indexStore != null) { + indexStore.clearInView(edge, this); + } + if (timeIndexStore != null) { + timeIndexStore.clearInView(edge, this); + } + } + } + } + } + + /** + * Bulk add edges to the view. This is more efficient than adding edges one by + * one as it updates stats in bulk and increments version only once. + */ + private void bulkAddEdges(BitSet edgesToAdd) { + // Update edge bit vector + int addedCount = edgesToAdd.cardinality(); + edgeBitVector.or(edgesToAdd); + edgeCount += addedCount; + + // Update type counts and mutual edge counts + for (int i = edgesToAdd.nextSetBit(0); i >= 0; i = edgesToAdd.nextSetBit(i + 1)) { + EdgeImpl edge = getEdge(i); + if (edge == null) { + continue; // SAFETY: Skip if edge no longer exists in store + } + + int type = edge.type; + ensureTypeCountArrayCapacity(type); + typeCounts[type]++; + + if (edge.isMutual() && !edge.isSelfLoop()) { + EdgeImpl reverseEdge = graphStore.edgeStore.get(edge.target, edge.source, edge.type, false); + if (reverseEdge != null && containsEdge(reverseEdge)) { + mutualEdgeTypeCounts[type]++; + mutualEdgesCount++; + } + } + } + + incrementEdgeVersion(); + + // Bulk update indexes + IndexStore indexStore = graphStore.edgeTable.store.indexStore; + TimeIndexStore timeIndexStore = graphStore.timeStore.edgeIndexStore; + + if (indexStore != null || timeIndexStore != null) { + for (int i = edgesToAdd.nextSetBit(0); i >= 0; i = edgesToAdd.nextSetBit(i + 1)) { + EdgeImpl edge = getEdge(i); + if (edge != null) { // SAFETY: Skip if edge no longer exists + if (indexStore != null) { + indexStore.indexInView(edge, this); + } + if (timeIndexStore != null) { + timeIndexStore.indexInView(edge, this); + } + } + } + } + } + + /** + * Special bulk remove for the not() operation. This updates the bit vector and + * stats but does NOT increment version or update indexes (those are handled + * separately in not()). + */ + private void bulkRemoveEdgesForNot(BitSet edgesToRemove) { + // Update edge bit vector + int removedCount = edgesToRemove.cardinality(); + edgeBitVector.andNot(edgesToRemove); + edgeCount -= removedCount; + + // Update type counts and mutual edge counts + for (int i = edgesToRemove.nextSetBit(0); i >= 0; i = edgesToRemove.nextSetBit(i + 1)) { + EdgeImpl edge = getEdge(i); + if (edge == null) { + continue; // SAFETY: Skip if edge no longer exists in store + } + + int type = edge.type; + ensureTypeCountArrayCapacity(type); + typeCounts[type]--; + + if (edge.isMutual() && !edge.isSelfLoop()) { + EdgeImpl reverseEdge = graphStore.edgeStore.get(edge.target, edge.source, edge.type, false); + if (reverseEdge != null && containsEdge(reverseEdge)) { + mutualEdgeTypeCounts[type]--; + mutualEdgesCount--; + } + } + } + // Note: Version increment and index updates are handled by the caller (not() + // method) + } + public int getNodeCount() { if (nodeView) { return nodeCount; @@ -687,33 +963,11 @@ protected void destroyAllObservers() { } protected void ensureNodeVectorSize(NodeImpl node) { - int sid = node.storeId; - if (sid >= nodeBitVector.size()) { - int newSize = Math.min(Math - .max(sid + 1, (int) (sid * GraphStoreConfiguration.VIEW_GROWING_FACTOR)), Integer.MAX_VALUE); - nodeBitVector = growBitVector(nodeBitVector, newSize); - } - } - - private void ensureNodeVectorSize(int size) { - if (size > nodeBitVector.size()) { - nodeBitVector = growBitVector(nodeBitVector, size); - } - } - - private void ensureEdgeVectorSize(int size) { - if (size > edgeBitVector.size()) { - edgeBitVector = growBitVector(edgeBitVector, size); - } + // BitSet automatically grows as needed, no manual resizing required } protected void ensureEdgeVectorSize(EdgeImpl edge) { - int sid = edge.storeId; - if (sid >= edgeBitVector.size()) { - int newSize = Math.min(Math - .max(sid + 1, (int) (sid * GraphStoreConfiguration.VIEW_GROWING_FACTOR)), Integer.MAX_VALUE); - edgeBitVector = growBitVector(edgeBitVector, newSize); - } + // BitSet automatically grows as needed, no manual resizing required } protected void setEdgeType(EdgeImpl edgeImpl, int oldType, boolean wasMutual) { @@ -779,13 +1033,10 @@ private void removeEdge(EdgeImpl edgeImpl) { if (indexStore != null) { indexStore.clearInView(edgeImpl, this); } - } - - private BitVector growBitVector(BitVector bitVector, int size) { - long[] elements = bitVector.elements(); - long[] newElements = QuickBitVector.makeBitVector(size, 1); - System.arraycopy(elements, 0, newElements, 0, elements.length); - return new BitVector(newElements, size); + TimeIndexStore timeIndexStore = graphStore.timeStore.edgeIndexStore; + if (timeIndexStore != null) { + timeIndexStore.clearInView(edgeImpl, this); + } } private NodeImpl getNode(int id) { diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index f50449bb..721b41bd 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -15,7 +15,7 @@ */ package org.gephi.graph.impl; -import cern.colt.bitvector.BitVector; +import java.util.BitSet; import it.unimi.dsi.fastutil.booleans.BooleanArrayList; import it.unimi.dsi.fastutil.booleans.BooleanOpenHashSet; import it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap; @@ -667,8 +667,8 @@ private GraphViewImpl deserializeGraphView(final DataInput is) throws IOExceptio int storeId = (Integer) deserialize(is); int nodeCount = (Integer) deserialize(is); int edgeCount = (Integer) deserialize(is); - BitVector nodeCountVector = (BitVector) deserialize(is); - BitVector edgeCountVector = (BitVector) deserialize(is); + BitSet nodeCountVector = (BitSet) deserialize(is); + BitSet edgeCountVector = (BitSet) deserialize(is); int[] typeCounts = (int[]) deserialize(is); int[] mutualEdgeTypeCounts = (int[]) deserialize(is); int mutualEdgesCount = (Integer) deserialize(is); @@ -695,15 +695,36 @@ private GraphViewImpl deserializeGraphView(final DataInput is) throws IOExceptio return view; } - private void serializeBitVector(final DataOutput out, final BitVector bitVector) throws IOException { - serialize(out, bitVector.size()); - serialize(out, bitVector.elements()); + // Made compatible with legacy BitVector serialization, which was in place until + // version 0.8.1 + public void serializeBitSet(final DataOutput out, final BitSet bitSet) throws IOException { + // BitSet.length() returns the index of the highest set bit + 1 + // This gives us the logical size (0 if empty) + int size = bitSet.length(); + + serialize(out, size); + + // Get the long array from BitSet + long[] words = bitSet.toLongArray(); + + // Calculate how many longs BitVector would use for this size + int requiredLongs = (size + 63) / 64; + + // Create array with the exact required size (matching BitVector format) + long[] elements = new long[requiredLongs]; + + // Copy the BitSet data + System.arraycopy(words, 0, elements, 0, Math.min(words.length, requiredLongs)); + + serialize(out, elements); } - private BitVector deserializeBitVector(final DataInput is) throws IOException, ClassNotFoundException { + public BitSet deserializeBitSet(final DataInput is) throws IOException, ClassNotFoundException { int size = (Integer) deserialize(is); long[] elements = (long[]) deserialize(is); - return new BitVector(elements, size); + + // BitSet.valueOf() handles the long array correctly + return BitSet.valueOf(elements); } private void serializeGraphStoreConfiguration(final DataOutput out) throws IOException { @@ -1558,10 +1579,10 @@ protected void serialize(final DataOutput out, final Object obj) throws IOExcept GraphViewImpl b = (GraphViewImpl) obj; out.write(GRAPH_VIEW); serializeGraphView(out, b); - } else if (obj instanceof BitVector) { - BitVector bv = (BitVector) obj; + } else if (obj instanceof BitSet) { + BitSet bs = (BitSet) obj; out.write(BIT_VECTOR); - serializeBitVector(out, bv); + serializeBitSet(out, bs); } else if (obj instanceof GraphVersion) { GraphVersion b = (GraphVersion) obj; out.write(GRAPH_VERSION); @@ -2122,7 +2143,7 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce ret = deserializeGraphView(is); break; case BIT_VECTOR: - ret = deserializeBitVector(is); + ret = deserializeBitSet(is); break; case GRAPH_STORE_CONFIGURATION: ret = deserializeGraphStoreConfiguration(is); diff --git a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java index ad340afb..a3352163 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewDecoratorTest.java @@ -786,6 +786,7 @@ public void testIsIncident() { Edge edge = graphStore.factory.newEdge("edge", n1, n1, EdgeTypeStore.NULL_LABEL, 1.0, true); graphStore.addEdge(edge); + view.addEdge(edge); // Explicitly add edge to view Assert.assertTrue(graph.isIncident(edge, graph.getEdge("0"))); } diff --git a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java index 875f6b23..e9e46e84 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java @@ -15,7 +15,10 @@ */ package org.gephi.graph.impl; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; @@ -507,4 +510,673 @@ public void testEdgeViewSetEdgeTypeMutualEdges() { Assert.assertEquals(view.getUndirectedEdgeCount(0), 0); Assert.assertEquals(view.getUndirectedEdgeCount(1), 1); } + + @Test + public void testCopyConstructor() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStoreWithMutualEdge(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + view.fill(); + + // Original view should have mutual edges counted + int originalEdgeCount = view.getEdgeCount(); + int originalMutualCount = view.mutualEdgesCount; + int originalUndirectedCount = view.getUndirectedEdgeCount(); + + // Create a copy using the copy constructor + GraphViewImpl copiedView = new GraphViewImpl(view, true, true); + + // Verify + Assert.assertEquals(copiedView.getNodeCount(), view + .getNodeCount(), "Node count should be the same in copied view"); + Assert.assertEquals(copiedView.nodeBitVector, view.nodeBitVector, "Node bit vector should be the same in copied view"); + Assert.assertEquals(copiedView.edgeBitVector, view.edgeBitVector, "Edge bit vector should be the same in copied view"); + + // Verify that mutualEdgesCount was copied correctly + Assert.assertEquals(copiedView.mutualEdgesCount, originalMutualCount, "mutualEdgesCount should be copied in copy constructor"); + Assert.assertEquals(copiedView.getEdgeCount(), originalEdgeCount); + Assert.assertEquals(copiedView + .getUndirectedEdgeCount(), originalUndirectedCount, "getUndirectedEdgeCount() should return correct value after copy"); + } + + @Test + public void testFilledViewRequiresExplicitAdd() { + // Test that users must explicitly add edges to filled views + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + view.fill(); + int initialEdgeCount = view.getEdgeCount(); + + // Add a new edge to the main graph store + NodeImpl n1 = graphStore.getNode("0"); + NodeImpl n2 = graphStore.getNode("1"); + EdgeImpl newEdge = new EdgeImpl("newEdge", n1, n2, 0, 1.0, true); + graphStore.addEdge(newEdge); + + // Edge should not be in view yet + Assert.assertFalse(view.containsEdge(newEdge)); + + // Explicitly add the edge to the view + boolean added = view.addEdge(newEdge); + + // Now it should be in the view + Assert.assertTrue(added, "addEdge should return true"); + Assert.assertTrue(view.containsEdge(newEdge), "Edge should be in view after explicit add"); + Assert.assertEquals(view.getEdgeCount(), initialEdgeCount + 1); + } + + // ========== Tests for Mutual Edge Counts in Bulk Operations ========== + + @Test + public void testIntersectionWithMutualEdges() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStoreWithMutualEdge(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); + GraphViewImpl view2 = store.createView(); + + // Fill both views + view1.fill(); + view2.fill(); + + // Initial state: both views have mutual edges (mutual count is 1 for a pair) + Assert.assertEquals(view1.mutualEdgesCount, 1, "View1 should have mutual count of 1"); + Assert.assertEquals(view1.getEdgeCount(), 2, "View1 should have 2 edges"); + Assert.assertEquals(view1.getUndirectedEdgeCount(), 1, "View1 should have 1 undirected edge"); + + // Remove one of the mutual edges from view2 + EdgeImpl e1 = graphStore.getEdge("1"); + view2.removeEdge(e1); + + // After removing one mutual edge, view2 should have no mutual edges + Assert.assertEquals(view2.mutualEdgesCount, 0, "View2 should have 0 mutual edges after removal"); + Assert.assertEquals(view2.getEdgeCount(), 1, "View2 should have 1 edge"); + Assert.assertEquals(view2.getUndirectedEdgeCount(), 1, "View2 should have 1 undirected edge"); + + // Intersection should result in view1 losing its mutual edge status + view1.intersection(view2); + + Assert.assertEquals(view1.mutualEdgesCount, 0, "View1 should have 0 mutual edges after intersection"); + Assert.assertEquals(view1.getEdgeCount(), 1, "View1 should have 1 edge total"); + Assert.assertEquals(view1.getUndirectedEdgeCount(), 1, "View1 should have 1 undirected edge"); + } + + @Test + public void testUnionWithMutualEdges() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStoreWithMutualEdge(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); + GraphViewImpl view2 = store.createView(); + + EdgeImpl e0 = graphStore.getEdge("0"); + EdgeImpl e1 = graphStore.getEdge("1"); + NodeImpl n1 = e0.getSource(); + NodeImpl n2 = e0.getTarget(); + + // View1 has only one edge of the mutual pair + view1.addNode(n1); + view1.addNode(n2); + view1.addEdge(e0); + + // View2 has only the other edge of the mutual pair + view2.addNode(n1); + view2.addNode(n2); + view2.addEdge(e1); + + // Neither view should have mutual edges yet + Assert.assertEquals(view1.mutualEdgesCount, 0, "View1 should have 0 mutual edges initially"); + Assert.assertEquals(view2.mutualEdgesCount, 0, "View2 should have 0 mutual edges initially"); + + // Union should create mutual edges (mutual count is 1 for a pair) + view1.union(view2); + + Assert.assertEquals(view1.mutualEdgesCount, 1, "View1 should have mutual count of 1 after union"); + Assert.assertEquals(view1.getEdgeCount(), 2, "View1 should have 2 edges total"); + Assert.assertEquals(view1.getUndirectedEdgeCount(), 1, "View1 should have 1 undirected edge"); + } + + @Test + public void testNotWithMutualEdges() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStoreWithMutualEdge(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + EdgeImpl e0 = graphStore.getEdge("0"); + EdgeImpl e1 = graphStore.getEdge("1"); + NodeImpl n1 = e0.getSource(); + NodeImpl n2 = e0.getTarget(); + + // Add only one edge of the mutual pair + view.addNode(n1); + view.addNode(n2); + view.addEdge(e0); + + Assert.assertEquals(view.mutualEdgesCount, 0, "View should have 0 mutual edges initially"); + Assert.assertEquals(view.getEdgeCount(), 1, "View should have 1 edge"); + Assert.assertEquals(view.getNodeCount(), 2, "View should have 2 nodes"); + + // NOT operation flips both nodes and edges + // Since there are only 2 nodes total in the graph, after NOT we have 0 nodes + // Edges without endpoints get removed, so we end up with 0 edges + view.not(); + + Assert.assertEquals(view.getNodeCount(), 0, "View should have 0 nodes after NOT (graph has 2 nodes total)"); + Assert.assertEquals(view.getEdgeCount(), 0, "View should have 0 edges after NOT (no nodes, so no edges)"); + } + + // ========== Tests for Multi-Type Edge Counts in Bulk Operations ========== + + @Test + public void testIntersectionMultipleEdgeTypes() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); + GraphViewImpl view2 = store.createView(); + + // Fill both views + view1.fill(); + view2.fill(); + + // Verify initial state has multiple edge types + int type0CountInitial = view1.getEdgeCount(0); + int type1CountInitial = view1.getEdgeCount(1); + Assert.assertTrue(type0CountInitial > 0, "Should have type 0 edges"); + Assert.assertTrue(type1CountInitial > 0, "Should have type 1 edges"); + + // Remove all type 0 edges from view2 + for (Edge e : graphStore.getEdges().toArray()) { + if (e.getType() == 0) { + view2.removeEdge(e); + } + } + + Assert.assertEquals(view2.getEdgeCount(0), 0, "View2 should have 0 type 0 edges"); + Assert.assertEquals(view2.getEdgeCount(1), type1CountInitial, "View2 should still have all type 1 edges"); + + // Intersection should remove all type 0 edges from view1 + view1.intersection(view2); + + Assert.assertEquals(view1.getEdgeCount(0), 0, "View1 should have 0 type 0 edges after intersection"); + Assert.assertEquals(view1 + .getEdgeCount(1), type1CountInitial, "View1 should have all type 1 edges after intersection"); + int type2CountInitial = view1.getEdgeCount(2); + Assert.assertEquals(view1 + .getEdgeCount(), type1CountInitial + type2CountInitial, "Total edge count should match sum of type 1 and type 2"); + } + + @Test + public void testUnionMultipleEdgeTypes() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); + GraphViewImpl view2 = store.createView(); + + // Add only type 0 edges to view1 + for (Node n : graphStore.getNodes()) { + view1.addNode(n); + } + for (Edge e : graphStore.getEdges().toArray()) { + if (e.getType() == 0) { + view1.addEdge(e); + } + } + + // Add only type 1 and type 2 edges to view2 + for (Node n : graphStore.getNodes()) { + view2.addNode(n); + } + for (Edge e : graphStore.getEdges().toArray()) { + if (e.getType() == 1 || e.getType() == 2) { + view2.addEdge(e); + } + } + + int type0Count = view1.getEdgeCount(0); + int type1Count = view2.getEdgeCount(1); + int type2Count = view2.getEdgeCount(2); + + Assert.assertTrue(type0Count > 0, "View1 should have type 0 edges"); + Assert.assertEquals(view2.getEdgeCount(0), 0, "View2 should have no type 0 edges"); + Assert.assertTrue(type1Count > 0, "View2 should have type 1 edges"); + Assert.assertTrue(type2Count > 0, "View2 should have type 2 edges"); + + // Union should combine both types + view1.union(view2); + + Assert.assertEquals(view1.getEdgeCount(0), type0Count, "View1 should have all type 0 edges after union"); + Assert.assertEquals(view1.getEdgeCount(1), type1Count, "View1 should have all type 1 edges after union"); + Assert.assertEquals(view1.getEdgeCount(2), type2Count, "View1 should have all type 2 edges after union"); + Assert.assertEquals(view1 + .getEdgeCount(), type0Count + type1Count + type2Count, "Total should be sum of all types"); + } + + @Test + public void testNotMultipleEdgeTypes() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + // Add all nodes but only type 0 edges + for (Node n : graphStore.getNodes()) { + view.addNode(n); + } + for (Edge e : graphStore.getEdges().toArray()) { + if (e.getType() == 0) { + view.addEdge(e); + } + } + + int type0Count = view.getEdgeCount(0); + int nodeCount = view.getNodeCount(); + int totalNodesInStore = graphStore.getNodeCount(); + + Assert.assertTrue(type0Count > 0, "View should have type 0 edges"); + + // NOT operation flips both nodes and edges + // Since we have all nodes, after NOT we have 0 nodes + // All edges get removed because they have no valid endpoints + view.not(); + + Assert.assertEquals(view + .getNodeCount(), totalNodesInStore - nodeCount, "View should have inverted node count after NOT"); + Assert.assertEquals(view + .getEdgeCount(), 0, "View should have 0 edges after NOT (no nodes means no valid edges)"); + } + + // ========== Tests for Empty View Edge Cases ========== + + @Test + public void testIntersectionWithEmptyView() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); + GraphViewImpl view2 = store.createView(); // Empty view + + // Fill view1 + view1.fill(); + int initialNodeCount = view1.getNodeCount(); + int initialEdgeCount = view1.getEdgeCount(); + + Assert.assertTrue(initialNodeCount > 0, "View1 should have nodes"); + Assert.assertTrue(initialEdgeCount > 0, "View1 should have edges"); + Assert.assertEquals(view2.getNodeCount(), 0, "View2 should be empty"); + Assert.assertEquals(view2.getEdgeCount(), 0, "View2 should be empty"); + + // Intersection with empty view should result in empty view1 + view1.intersection(view2); + + Assert.assertEquals(view1.getNodeCount(), 0, "View1 should be empty after intersection with empty view"); + Assert.assertEquals(view1.getEdgeCount(), 0, "View1 should have no edges after intersection with empty view"); + } + + @Test + public void testIntersectionOfEmptyView() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); // Empty view + GraphViewImpl view2 = store.createView(); + + // Fill view2 + view2.fill(); + + Assert.assertEquals(view1.getNodeCount(), 0, "View1 should be empty"); + Assert.assertTrue(view2.getNodeCount() > 0, "View2 should have nodes"); + + // Intersection of empty view with filled view should stay empty + view1.intersection(view2); + + Assert.assertEquals(view1.getNodeCount(), 0, "View1 should still be empty after intersection"); + Assert.assertEquals(view1.getEdgeCount(), 0, "View1 should still have no edges after intersection"); + } + + @Test + public void testUnionWithEmptyView() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); + GraphViewImpl view2 = store.createView(); // Empty view + + // Fill view1 + view1.fill(); + int initialNodeCount = view1.getNodeCount(); + int initialEdgeCount = view1.getEdgeCount(); + + Assert.assertTrue(initialNodeCount > 0, "View1 should have nodes"); + Assert.assertTrue(initialEdgeCount > 0, "View1 should have edges"); + Assert.assertEquals(view2.getNodeCount(), 0, "View2 should be empty"); + + // Union with empty view should not change view1 + view1.union(view2); + + Assert.assertEquals(view1.getNodeCount(), initialNodeCount, "View1 node count should not change"); + Assert.assertEquals(view1.getEdgeCount(), initialEdgeCount, "View1 edge count should not change"); + } + + @Test + public void testUnionOfEmptyView() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); // Empty view + GraphViewImpl view2 = store.createView(); + + // Fill view2 + view2.fill(); + int view2NodeCount = view2.getNodeCount(); + int view2EdgeCount = view2.getEdgeCount(); + + Assert.assertEquals(view1.getNodeCount(), 0, "View1 should be empty"); + Assert.assertTrue(view2NodeCount > 0, "View2 should have nodes"); + + // Union of empty view with filled view should fill view1 + view1.union(view2); + + Assert.assertEquals(view1.getNodeCount(), view2NodeCount, "View1 should have same node count as view2"); + Assert.assertEquals(view1.getEdgeCount(), view2EdgeCount, "View1 should have same edge count as view2"); + } + + @Test + public void testNotOnEmptyView() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); // Empty view + + int totalNodes = graphStore.getNodeCount(); + int totalEdges = graphStore.getEdgeCount(); + + Assert.assertEquals(view.getNodeCount(), 0, "View should be empty initially"); + Assert.assertEquals(view.getEdgeCount(), 0, "View should have no edges initially"); + + // NOT on empty view should fill it completely + view.not(); + + Assert.assertEquals(view.getNodeCount(), totalNodes, "View should have all nodes after NOT"); + Assert.assertEquals(view.getEdgeCount(), totalEdges, "View should have all edges after NOT"); + + // Verify all elements are present + for (Node n : graphStore.getNodes()) { + Assert.assertTrue(view.containsNode((NodeImpl) n), "View should contain all nodes after NOT"); + } + for (Edge e : graphStore.getEdges()) { + Assert.assertTrue(view.containsEdge((EdgeImpl) e), "View should contain all edges after NOT"); + } + } + + @Test + public void testIntersectionBothEmpty() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); // Empty + GraphViewImpl view2 = store.createView(); // Empty + + // Both views empty + Assert.assertEquals(view1.getNodeCount(), 0, "View1 should be empty"); + Assert.assertEquals(view2.getNodeCount(), 0, "View2 should be empty"); + + // Intersection of two empty views should stay empty + view1.intersection(view2); + + Assert.assertEquals(view1.getNodeCount(), 0, "View1 should still be empty"); + Assert.assertEquals(view1.getEdgeCount(), 0, "View1 should still have no edges"); + } + + @Test + public void testUnionBothEmpty() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view1 = store.createView(); // Empty + GraphViewImpl view2 = store.createView(); // Empty + + // Both views empty + Assert.assertEquals(view1.getNodeCount(), 0, "View1 should be empty"); + Assert.assertEquals(view2.getNodeCount(), 0, "View2 should be empty"); + + // Union of two empty views should stay empty + view1.union(view2); + + Assert.assertEquals(view1.getNodeCount(), 0, "View1 should still be empty"); + Assert.assertEquals(view1.getEdgeCount(), 0, "View1 should still have no edges"); + } + + // ========== Tests for Retain Operations ========== + + @Test + public void testRetainNodesBasic() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + view.fill(); + int initialNodeCount = view.getNodeCount(); + int initialEdgeCount = view.getEdgeCount(); + + // Retain all nodes - should return false (no change) + boolean changed = view.retainNodes(graphStore.getNodes().toCollection()); + Assert.assertFalse(changed, "Retaining all nodes should return false"); + Assert.assertEquals(view.getNodeCount(), initialNodeCount, "Node count should not change"); + Assert.assertEquals(view.getEdgeCount(), initialEdgeCount, "Edge count should not change"); + + // Retain subset of nodes + NodeImpl n1 = graphStore.getNode("0"); + NodeImpl n2 = graphStore.getNode("1"); + changed = view.retainNodes(Arrays.asList(n1, n2)); + + Assert.assertTrue(changed, "Retaining subset should return true"); + Assert.assertEquals(view.getNodeCount(), 2, "Should have exactly 2 nodes"); + Assert.assertTrue(view.containsNode(n1), "Should contain node 0"); + Assert.assertTrue(view.containsNode(n2), "Should contain node 1"); + } + + @Test + public void testRetainNodesEmpty() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + view.fill(); + + // Retain empty collection - should clear everything + boolean changed = view.retainNodes(Collections.emptyList()); + + Assert.assertTrue(changed, "Retaining empty list should return true"); + Assert.assertEquals(view.getNodeCount(), 0, "Should have no nodes"); + Assert.assertEquals(view.getEdgeCount(), 0, "Should have no edges"); + } + + @Test + public void testRetainEdgesBasic() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(false, true); // Edge view only + + view.fill(); + int initialEdgeCount = view.getEdgeCount(); + + // Retain all edges - should return false (no change) + boolean changed = view.retainEdges(graphStore.getEdges().toCollection()); + Assert.assertFalse(changed, "Retaining all edges should return false"); + Assert.assertEquals(view.getEdgeCount(), initialEdgeCount, "Edge count should not change"); + + // Retain subset of edges + EdgeImpl e1 = graphStore.getEdge("0"); + EdgeImpl e2 = graphStore.getEdge("1"); + changed = view.retainEdges(Arrays.asList(e1, e2)); + + Assert.assertTrue(changed, "Retaining subset should return true"); + Assert.assertEquals(view.getEdgeCount(), 2, "Should have exactly 2 edges"); + Assert.assertTrue(view.containsEdge(e1), "Should contain edge 0"); + Assert.assertTrue(view.containsEdge(e2), "Should contain edge 1"); + } + + @Test + public void testRetainEdgesEmpty() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(false, true); // Edge view only + + view.fill(); + + // Retain empty collection - should clear all edges + boolean changed = view.retainEdges(Collections.emptyList()); + + Assert.assertTrue(changed, "Retaining empty list should return true"); + Assert.assertEquals(view.getEdgeCount(), 0, "Should have no edges"); + } + + @Test + public void testRetainNodesWithMutualEdges() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStoreWithMutualEdge(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + view.fill(); + + EdgeImpl e0 = graphStore.getEdge("0"); + EdgeImpl e1 = graphStore.getEdge("1"); + NodeImpl n1 = e0.getSource(); + NodeImpl n2 = e0.getTarget(); + + // Initial state: view has mutual edges + Assert.assertEquals(view.mutualEdgesCount, 1, "View should have mutual count of 1"); + Assert.assertEquals(view.getEdgeCount(), 2, "View should have 2 edges"); + + // Retain both nodes - mutual edges should remain + boolean changed = view.retainNodes(Arrays.asList(n1, n2)); + + Assert.assertFalse(changed, "Retaining all nodes should return false"); + Assert.assertEquals(view.mutualEdgesCount, 1, "Mutual edges should remain"); + Assert.assertEquals(view.getEdgeCount(), 2, "Should still have 2 edges"); + } + + @Test + public void testRetainEdgesWithMultipleTypes() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(false, true); // Edge view only + + view.fill(); + + int type0Count = view.getEdgeCount(0); + int type1Count = view.getEdgeCount(1); + int type2Count = view.getEdgeCount(2); + + Assert.assertTrue(type0Count > 0, "Should have type 0 edges"); + Assert.assertTrue(type1Count > 0, "Should have type 1 edges"); + Assert.assertTrue(type2Count > 0, "Should have type 2 edges"); + + // Collect only type 0 edges to retain + List type0Edges = new ArrayList<>(); + for (Edge e : graphStore.getEdges().toArray()) { + if (e.getType() == 0) { + type0Edges.add(e); + } + } + + // Retain only type 0 edges + boolean changed = view.retainEdges(type0Edges); + + Assert.assertTrue(changed, "Should have removed edges"); + Assert.assertEquals(view.getEdgeCount(0), type0Count, "Should still have all type 0 edges"); + Assert.assertEquals(view.getEdgeCount(1), 0, "Should have no type 1 edges"); + Assert.assertEquals(view.getEdgeCount(2), 0, "Should have no type 2 edges"); + Assert.assertEquals(view.getEdgeCount(), type0Count, "Total should match type 0 count"); + } + + @Test + public void testRetainNodesWithMultipleEdgeTypes() { + GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + view.fill(); + + int initialType0Count = view.getEdgeCount(0); + int initialType1Count = view.getEdgeCount(1); + + // Retain subset of nodes + List nodesToRetain = new ArrayList<>(); + int count = 0; + for (Node n : graphStore.getNodes()) { + nodesToRetain.add(n); + count++; + if (count >= 5) { + break; // Keep first 5 nodes + } + } + + boolean changed = view.retainNodes(nodesToRetain); + + Assert.assertTrue(changed, "Should have removed nodes"); + Assert.assertEquals(view.getNodeCount(), 5, "Should have exactly 5 nodes"); + + // Edge counts should have decreased but type tracking should still be correct + int newType0Count = view.getEdgeCount(0); + int newType1Count = view.getEdgeCount(1); + + Assert.assertTrue(newType0Count <= initialType0Count, "Type 0 count should not increase"); + Assert.assertTrue(newType1Count <= initialType1Count, "Type 1 count should not increase"); + Assert.assertEquals(view.getEdgeCount(), newType0Count + newType1Count + view + .getEdgeCount(2), "Total edge count should match sum of types"); + } + + @Test + public void testRetainNodesLargeScale() { + // Test bulk operation performance with larger dataset + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(); + + view.fill(); + int totalNodes = view.getNodeCount(); + + // Retain half the nodes + List nodesToRetain = new ArrayList<>(); + int count = 0; + for (Node n : graphStore.getNodes()) { + if (count % 2 == 0) { + nodesToRetain.add(n); + } + count++; + } + + boolean changed = view.retainNodes(nodesToRetain); + + Assert.assertTrue(changed, "Should have removed nodes"); + Assert.assertTrue(view.getNodeCount() <= totalNodes / 2 + 1, "Should have roughly half the nodes"); + Assert.assertTrue(view.getNodeCount() >= totalNodes / 2 - 1, "Should have roughly half the nodes"); + + // Verify all retained nodes are in the view + for (Node n : nodesToRetain) { + Assert.assertTrue(view.containsNode((NodeImpl) n), "Retained node should be in view"); + } + } + + @Test + public void testRetainEdgesOnlyView() { + // Test retain edges when nodeView=false, edgeView=true + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(false, true); + + view.fill(); + + List edgesToRetain = new ArrayList<>(); + int count = 0; + for (Edge e : graphStore.getEdges().toArray()) { + edgesToRetain.add(e); + count++; + if (count >= 10) { + break; + } + } + + boolean changed = view.retainEdges(edgesToRetain); + + Assert.assertTrue(changed, "Should have removed edges"); + Assert.assertEquals(view.getEdgeCount(), 10, "Should have exactly 10 edges"); + + for (Edge e : edgesToRetain) { + Assert.assertTrue(view.containsEdge((EdgeImpl) e), "Retained edge should be in view"); + } + } } diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index c56c346f..561a28c6 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -15,7 +15,6 @@ */ package org.gephi.graph.impl; -import cern.colt.bitvector.BitVector; import it.unimi.dsi.fastutil.booleans.BooleanArrayList; import it.unimi.dsi.fastutil.booleans.BooleanOpenHashSet; import it.unimi.dsi.fastutil.bytes.Byte2ObjectOpenHashMap; @@ -49,12 +48,14 @@ import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Random; import java.util.Set; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.GraphModel; @@ -315,14 +316,14 @@ public void testGraphView() throws IOException, ClassNotFoundException { } @Test - public void testBitVector() throws IOException, ClassNotFoundException { - BitVector bitVector = new BitVector(10); + public void testBitSet() throws IOException, ClassNotFoundException { + BitSet bitVector = new BitSet(10); bitVector.set(1); bitVector.set(4); Serialization ser = new Serialization(null); byte[] buf = ser.serialize(bitVector); - BitVector l = (BitVector) ser.deserialize(buf); + BitSet l = (BitSet) ser.deserialize(buf); Assert.assertEquals(bitVector, l); } @@ -1266,4 +1267,27 @@ public void serializeGraphModel(DataOutput out, GraphModelImpl model) throws IOE GraphModelImpl read = ser.deserializeGraphModelWithoutVersionPrefix(dio.reset(bytes), Serialization.VERSION); Assert.assertTrue(read.deepEquals(gm)); } + + @Test + public void testBitVectorEqual() throws Exception { + Serialization ser = new Serialization(); + + Random random = new Random(); + for (int i = 0; i < 20000; i += 97) { + BitSet bs = new BitSet(i); + + int p = random.nextInt(99) + 1; + for (int j = 0; j < i; j++) { + if (random.nextInt(100) < p) { + bs.set(j); + } + } + DataInputOutput dio = new DataInputOutput(); + ser.serializeBitSet(dio, bs); + byte[] bytes = dio.toByteArray(); + + BitSet deserializedBs = ser.deserializeBitSet(dio.reset(bytes)); + Assert.assertEquals(bs, deserializedBs); + } + } } From fbcea172aaf3c19549baa1e592e98b41debec18b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Oct 2025 16:49:35 +0100 Subject: [PATCH 199/271] Add additional test to NodeStore --- .../java/org/gephi/graph/impl/NodeStoreTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/java/org/gephi/graph/impl/NodeStoreTest.java b/src/test/java/org/gephi/graph/impl/NodeStoreTest.java index 2c320bfd..09b89d40 100644 --- a/src/test/java/org/gephi/graph/impl/NodeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/NodeStoreTest.java @@ -98,6 +98,19 @@ public void testAddOtherStore() { nodeStore2.add(node); } + @Test + public void testMaxStoreId() { + NodeStore nodeStore = new NodeStore(); + Assert.assertEquals(nodeStore.maxStoreId(), 0); + NodeImpl node1 = new NodeImpl("0"); + NodeImpl node2 = new NodeImpl("1"); + nodeStore.add(node1); + Assert.assertEquals(nodeStore.maxStoreId(), 1); + nodeStore.add(node2); + Assert.assertEquals(nodeStore.maxStoreId(), 2); + Assert.assertEquals(node2.getStoreId(), 1); + } + @Test public void testGet() { NodeStore nodeStore = new NodeStore(); From c344fdf12ade6e4ced00caf96e873413053a883c Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Oct 2025 16:55:48 +0100 Subject: [PATCH 200/271] Minor issues fixed --- src/main/java/org/gephi/graph/impl/Serialization.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 721b41bd..efe44c19 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -444,8 +444,8 @@ private EdgeImpl deserializeEdge(DataInput is) throws IOException, ClassNotFound int sourceNewId = idMap.get(sourceId); int targetNewId = idMap.get(targetId); - if (sourceId == NULL_ID || targetId == NULL_ID) { - throw new IOException("The edge source of target can't be found"); + if (sourceNewId == NULL_ID || targetNewId == NULL_ID) { + throw new IOException("The edge source or target can't be found"); } NodeImpl source = model.store.nodeStore.get(sourceNewId); @@ -967,7 +967,7 @@ private IntervalMap deserializeIntervalMap(final DataInput is) throws IOExceptio } else if (mapClass.equals(String[].class)) { valueSet = new IntervalStringMap(intervals, (String[]) values); } else { - throw new RuntimeException("Unrecognized timestamp map class"); + throw new RuntimeException("Unrecognized interval map class"); } return valueSet; } From a9bde6d9d3445e27008cc83071e677715d8ba1e6 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Oct 2025 17:22:07 +0100 Subject: [PATCH 201/271] Fix other minor issues --- src/main/java/org/gephi/graph/api/AttributeUtils.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index 3dbacf0a..bb187dbb 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -115,7 +115,7 @@ public class AttributeUtils { private static final Map DATE_TIME_PRINTERS_BY_TIMEZONE; private static final Map DATE_TIME_PARSERS_BY_TIMEZONE; - // Collectio types to speedup lookup + // Collection types to speedup lookup private static final Set TYPED_LIST_TYPES; private static final Set TYPED_SET_TYPES; private static final Set TYPED_MAP_TYPES; @@ -355,7 +355,7 @@ public static String print(Object value, TimeFormat timeFormat, ZoneId zoneId) { return ((TimeMap) value).toString(timeFormat, zoneId); } if (value instanceof Instant) { - printDate((Instant) value, zoneId); + return value.toString(); } if (value.getClass().isArray()) { return printArray(value); @@ -814,8 +814,8 @@ private static List getStandardizedList(List list) { } private static Set getStandardizedSet(Set set) { - Class listClass = set.getClass(); - if (TYPED_LIST_TYPES.contains(listClass)) { + Class setClass = set.getClass(); + if (TYPED_SET_TYPES.contains(setClass)) { return set; } @@ -1276,7 +1276,7 @@ public static Object copy(Object obj) { // Instant if (typeClass.equals(Instant.class)) { - return Instant.from((Instant) obj); + return obj; } // Interval types: From c34f9506c6d13bc693320e2639bf8395c5a31ee4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Oct 2025 17:45:08 +0100 Subject: [PATCH 202/271] Fix minor issues --- .../java/org/gephi/graph/impl/ColumnStore.java | 16 +++++++++++++--- .../java/org/gephi/graph/impl/TableImpl.java | 3 +++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index 7eba744f..178d2115 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -387,15 +387,25 @@ public boolean deepEquals(ColumnStore obj) { return false; } } + if (itr2.hasNext()) { + return false; + } return true; } public int deepHashCode() { int hash = 3; hash = 11 * hash + (this.elementType != null ? this.elementType.hashCode() : 0); - ColumnStoreIterator itr = new ColumnStoreIterator(); - while (itr.hasNext()) { - hash = 11 * hash + itr.next().deepHashCode(); + lock(); + try { + for (int i = 0; i < length; i++) { + ColumnImpl c = columns[i]; + if (c != null) { + hash = 11 * hash + c.deepHashCode(); + } + } + } finally { + unlock(); } // TODO what about timestampmap return hash; diff --git a/src/main/java/org/gephi/graph/impl/TableImpl.java b/src/main/java/org/gephi/graph/impl/TableImpl.java index c66ccb4e..2629de0b 100644 --- a/src/main/java/org/gephi/graph/impl/TableImpl.java +++ b/src/main/java/org/gephi/graph/impl/TableImpl.java @@ -161,11 +161,13 @@ public ColumnImpl getColumn(int index) { @Override public ColumnImpl getColumn(String id) { + store.checkNonNullObject(id); return store.getColumn(id.toLowerCase()); } @Override public boolean hasColumn(String id) { + store.checkNonNullObject(id); return store.hasColumn(id.toLowerCase()); } @@ -184,6 +186,7 @@ public void removeColumn(Column column) { @Override public void removeColumn(String id) { + store.checkNonNullObject(id); store.removeColumn(id.toLowerCase()); } From 513ed2289172421d0c4ff6f349d7fd6e372ca543 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 26 Oct 2025 18:15:07 +0100 Subject: [PATCH 203/271] Fix another minor issue --- .../org/gephi/graph/impl/GraphFactoryImpl.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java b/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java index e54ccaec..6567c992 100644 --- a/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphFactoryImpl.java @@ -162,23 +162,15 @@ protected void setEdgeCounter(int count) { } private static boolean isNumeric(String str) { - if (str == null) { + if (str == null || str.isEmpty()) { return false; } - char[] data = str.toCharArray(); - if (data.length <= 0 || data.length > 9) { + try { + Integer.parseInt(str); + return true; + } catch (NumberFormatException e) { return false; } - int index = 0; - if (data[0] == '-' && data.length > 1) { - index = 1; - } - for (; index < data.length; index++) { - if (data[index] < '0' || data[index] > '9') { - return false; - } - } - return true; } public int deepHashCode() { From db937ae944076e188a1014c18417fb085a873f8a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 27 Oct 2025 19:55:28 +0100 Subject: [PATCH 204/271] Add missing validation --- src/main/java/org/gephi/graph/impl/EdgeStore.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index b03277ba..5183514d 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -1655,7 +1655,9 @@ public EdgeInOutMultiIterator(Iterator nodeIterator, boolean locking) @Override protected boolean moveToNextNode() { if (nodeIterator.hasNext()) { - initializeForNode(nodeIterator.next()); + NodeImpl node = nodeIterator.next(); + checkValidNodeObject(node); + initializeForNode(node); return true; } return false; From 5f5cd7bb3818cc01163122ea467a189662fa1d96 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 27 Oct 2025 21:24:09 +0100 Subject: [PATCH 205/271] Make graph view tests more resilient --- .../gephi/graph/impl/GraphViewImplTest.java | 181 +++++++++++++++++- 1 file changed, 175 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java index e9e46e84..377d3a6c 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java @@ -173,13 +173,34 @@ public void testViewIntersection() { view.intersection(view2); + // Positive assertions - expected elements ARE present Assert.assertTrue(view.containsNode(n1)); Assert.assertTrue(view.containsNode(n2)); Assert.assertTrue(view.containsEdge(e1)); + + // Negative assertions - elements not in intersection should be absent Assert.assertFalse(view.containsNode(n3)); Assert.assertFalse(view.containsNode(n4)); Assert.assertFalse(view.containsEdge(e2)); + // Exact count assertions + Assert.assertEquals(view.getNodeCount(), 2, "Should have exactly 2 nodes after intersection"); + Assert.assertEquals(view.getEdgeCount(), 1, "Should have exactly 1 edge after intersection"); + + // Verify no other elements from the graph are present + for (Node n : graphStore.getNodes()) { + if (n != n1 && n != n2) { + Assert.assertFalse(view + .containsNode((NodeImpl) n), "Node " + n.getId() + " should not be in view after intersection"); + } + } + for (Edge e : graphStore.getEdges()) { + if (e != e1) { + Assert.assertFalse(view + .containsEdge((EdgeImpl) e), "Edge " + e.getId() + " should not be in view after intersection"); + } + } + Assert.assertTrue(view2.deepEquals(view)); } @@ -193,6 +214,7 @@ public void testViewIntersectionEdgeView() { view.fill(); view2.fill(); + int totalEdges = graphStore.getEdgeCount(); EdgeImpl e1 = graphStore.getEdge("0"); EdgeImpl e2 = graphStore.getEdge("5"); @@ -201,8 +223,21 @@ public void testViewIntersectionEdgeView() { view.intersection(view2); + // Negative assertions - removed edges should be absent Assert.assertFalse(view.containsEdge(e1)); Assert.assertFalse(view.containsEdge(e2)); + + // Exact count assertion - intersection excludes both removed edges + Assert.assertEquals(view + .getEdgeCount(), totalEdges - 2, "Should have all edges except e1 and e2 after intersection"); + + // Verify all other edges are present + for (Edge e : graphStore.getEdges()) { + if (e != e1 && e != e2) { + Assert.assertTrue(view + .containsEdge((EdgeImpl) e), "Edge " + e.getId() + " should be in view after intersection"); + } + } } @Test @@ -215,6 +250,7 @@ public void testViewIntersectionNodeView() { view.fill(); view2.fill(); + int totalNodes = graphStore.getNodeCount(); EdgeImpl e1 = graphStore.getEdge("0"); EdgeImpl e2 = graphStore.getEdge("5"); NodeImpl s1 = e1.getSource(); @@ -224,9 +260,21 @@ public void testViewIntersectionNodeView() { view.intersection(view2); - Assert.assertFalse(view.containsEdge(e1)); + // Node intersection: s1 was removed from view2, so it should be absent Assert.assertFalse(view.containsNode(s1)); - Assert.assertTrue(view.containsEdge(e2)); + Assert.assertEquals(view.getNodeCount(), totalNodes - 1, "Should have all nodes except s1 after intersection"); + + // Edge intersection: e1 removed because s1 is gone (node view), e2 present + Assert.assertFalse(view.containsEdge(e1), "e1 should be absent (source node removed)"); + Assert.assertTrue(view.containsEdge(e2), "e2 should be present"); + + // Verify all other nodes are present + for (Node n : graphStore.getNodes()) { + if (n != s1) { + Assert.assertTrue(view + .containsNode((NodeImpl) n), "Node " + n.getId() + " should be in view after intersection"); + } + } } @Test @@ -253,12 +301,31 @@ public void testViewUnion() { view.union(view2); + // Positive assertions - expected elements ARE present Assert.assertTrue(view.containsNode(n1)); Assert.assertTrue(view.containsNode(n2)); Assert.assertTrue(view.containsEdge(e1)); Assert.assertTrue(view.containsNode(n3)); Assert.assertTrue(view.containsNode(n4)); Assert.assertTrue(view.containsEdge(e2)); + + // Exact count assertions - verify ONLY expected elements + Assert.assertEquals(view.getNodeCount(), 4, "Should have exactly 4 nodes after union"); + Assert.assertEquals(view.getEdgeCount(), 2, "Should have exactly 2 edges after union"); + + // Negative assertions - verify other graph elements are NOT present + for (Node n : graphStore.getNodes()) { + if (n != n1 && n != n2 && n != n3 && n != n4) { + Assert.assertFalse(view + .containsNode((NodeImpl) n), "Node " + n.getId() + " should not be in view after union"); + } + } + for (Edge e : graphStore.getEdges()) { + if (e != e1 && e != e2) { + Assert.assertFalse(view + .containsEdge((EdgeImpl) e), "Edge " + e.getId() + " should not be in view after union"); + } + } } @Test @@ -276,8 +343,20 @@ public void testViewUnionEdgeView() { view.union(view2); + // Positive assertions Assert.assertTrue(view.containsEdge(e1)); Assert.assertTrue(view.containsEdge(e2)); + + // Exact count assertion + Assert.assertEquals(view.getEdgeCount(), 2, "Should have exactly 2 edges after union"); + + // Negative assertions - verify other edges are NOT present + for (Edge e : graphStore.getEdges()) { + if (e != e1 && e != e2) { + Assert.assertFalse(view + .containsEdge((EdgeImpl) e), "Edge " + e.getId() + " should not be in view after union"); + } + } } @Test @@ -300,8 +379,24 @@ public void testViewUnionNodeView() { view.union(view2); + // Positive assertions Assert.assertTrue(view.containsEdge(e1)); - Assert.assertTrue(view.containsEdge(e2)); + Assert.assertTrue(view.containsEdge(e2), "e2 should be present (both endpoints are in union)"); + + // All 4 nodes should be present + Assert.assertTrue(view.containsNode(n1)); + Assert.assertTrue(view.containsNode(n2)); + Assert.assertTrue(view.containsNode(n3)); + Assert.assertTrue(view.containsNode(n4)); + Assert.assertEquals(view.getNodeCount(), 4, "Should have exactly 4 nodes after union"); + + // Verify no other nodes are present + for (Node n : graphStore.getNodes()) { + if (n != n1 && n != n2 && n != n3 && n != n4) { + Assert.assertFalse(view + .containsNode((NodeImpl) n), "Node " + n.getId() + " should not be in view after union"); + } + } } @Test @@ -586,6 +681,7 @@ public void testIntersectionWithMutualEdges() { Assert.assertEquals(view1.getUndirectedEdgeCount(), 1, "View1 should have 1 undirected edge"); // Remove one of the mutual edges from view2 + EdgeImpl e0 = graphStore.getEdge("0"); EdgeImpl e1 = graphStore.getEdge("1"); view2.removeEdge(e1); @@ -600,6 +696,10 @@ public void testIntersectionWithMutualEdges() { Assert.assertEquals(view1.mutualEdgesCount, 0, "View1 should have 0 mutual edges after intersection"); Assert.assertEquals(view1.getEdgeCount(), 1, "View1 should have 1 edge total"); Assert.assertEquals(view1.getUndirectedEdgeCount(), 1, "View1 should have 1 undirected edge"); + + // Verify which edge remains + Assert.assertTrue(view1.containsEdge(e0), "e0 should remain after intersection"); + Assert.assertFalse(view1.containsEdge(e1), "e1 should be absent after intersection"); } @Test @@ -634,6 +734,15 @@ public void testUnionWithMutualEdges() { Assert.assertEquals(view1.mutualEdgesCount, 1, "View1 should have mutual count of 1 after union"); Assert.assertEquals(view1.getEdgeCount(), 2, "View1 should have 2 edges total"); Assert.assertEquals(view1.getUndirectedEdgeCount(), 1, "View1 should have 1 undirected edge"); + + // Verify both edges are present + Assert.assertTrue(view1.containsEdge(e0), "e0 should be present after union"); + Assert.assertTrue(view1.containsEdge(e1), "e1 should be present after union"); + + // Verify only the expected nodes are present + Assert.assertEquals(view1.getNodeCount(), 2, "Should have exactly 2 nodes"); + Assert.assertTrue(view1.containsNode(n1)); + Assert.assertTrue(view1.containsNode(n2)); } @Test @@ -643,7 +752,6 @@ public void testNotWithMutualEdges() { GraphViewImpl view = store.createView(); EdgeImpl e0 = graphStore.getEdge("0"); - EdgeImpl e1 = graphStore.getEdge("1"); NodeImpl n1 = e0.getSource(); NodeImpl n2 = e0.getTarget(); @@ -681,6 +789,7 @@ public void testIntersectionMultipleEdgeTypes() { // Verify initial state has multiple edge types int type0CountInitial = view1.getEdgeCount(0); int type1CountInitial = view1.getEdgeCount(1); + int type2CountInitial = view1.getEdgeCount(2); Assert.assertTrue(type0CountInitial > 0, "Should have type 0 edges"); Assert.assertTrue(type1CountInitial > 0, "Should have type 1 edges"); @@ -700,9 +809,18 @@ public void testIntersectionMultipleEdgeTypes() { Assert.assertEquals(view1.getEdgeCount(0), 0, "View1 should have 0 type 0 edges after intersection"); Assert.assertEquals(view1 .getEdgeCount(1), type1CountInitial, "View1 should have all type 1 edges after intersection"); - int type2CountInitial = view1.getEdgeCount(2); + Assert.assertEquals(view1 + .getEdgeCount(2), type2CountInitial, "View1 should have all type 2 edges after intersection"); Assert.assertEquals(view1 .getEdgeCount(), type1CountInitial + type2CountInitial, "Total edge count should match sum of type 1 and type 2"); + + // Verify no type 0 edges are present + for (Edge e : graphStore.getEdges()) { + if (e.getType() == 0) { + Assert.assertFalse(view1.containsEdge((EdgeImpl) e), "Type 0 edge " + e + .getId() + " should not be in view after intersection"); + } + } } @Test @@ -749,6 +867,12 @@ public void testUnionMultipleEdgeTypes() { Assert.assertEquals(view1.getEdgeCount(2), type2Count, "View1 should have all type 2 edges after union"); Assert.assertEquals(view1 .getEdgeCount(), type0Count + type1Count + type2Count, "Total should be sum of all types"); + + // Verify all edges of each type are present + for (Edge e : graphStore.getEdges()) { + Assert.assertTrue(view1.containsEdge((EdgeImpl) e), "Edge " + e.getId() + " of type " + e + .getType() + " should be in view after union"); + } } @Test @@ -808,6 +932,16 @@ public void testIntersectionWithEmptyView() { Assert.assertEquals(view1.getNodeCount(), 0, "View1 should be empty after intersection with empty view"); Assert.assertEquals(view1.getEdgeCount(), 0, "View1 should have no edges after intersection with empty view"); + + // Verify all elements are absent + for (Node n : graphStore.getNodes()) { + Assert.assertFalse(view1.containsNode((NodeImpl) n), "Node " + n + .getId() + " should not be in view after intersection with empty view"); + } + for (Edge e : graphStore.getEdges()) { + Assert.assertFalse(view1.containsEdge((EdgeImpl) e), "Edge " + e + .getId() + " should not be in view after intersection with empty view"); + } } @Test @@ -828,6 +962,12 @@ public void testIntersectionOfEmptyView() { Assert.assertEquals(view1.getNodeCount(), 0, "View1 should still be empty after intersection"); Assert.assertEquals(view1.getEdgeCount(), 0, "View1 should still have no edges after intersection"); + + // Verify all elements are absent + for (Node n : graphStore.getNodes()) { + Assert.assertFalse(view1.containsNode((NodeImpl) n), "Node " + n + .getId() + " should not be in empty view after intersection"); + } } @Test @@ -851,6 +991,16 @@ public void testUnionWithEmptyView() { Assert.assertEquals(view1.getNodeCount(), initialNodeCount, "View1 node count should not change"); Assert.assertEquals(view1.getEdgeCount(), initialEdgeCount, "View1 edge count should not change"); + + // Verify all elements are still present + for (Node n : graphStore.getNodes()) { + Assert.assertTrue(view1.containsNode((NodeImpl) n), "Node " + n + .getId() + " should still be in view after union with empty view"); + } + for (Edge e : graphStore.getEdges()) { + Assert.assertTrue(view1.containsEdge((EdgeImpl) e), "Edge " + e + .getId() + " should still be in view after union with empty view"); + } } @Test @@ -873,6 +1023,14 @@ public void testUnionOfEmptyView() { Assert.assertEquals(view1.getNodeCount(), view2NodeCount, "View1 should have same node count as view2"); Assert.assertEquals(view1.getEdgeCount(), view2EdgeCount, "View1 should have same edge count as view2"); + + // Verify all elements are present + for (Node n : graphStore.getNodes()) { + Assert.assertTrue(view1.containsNode((NodeImpl) n), "Node " + n.getId() + " should be in view after union"); + } + for (Edge e : graphStore.getEdges()) { + Assert.assertTrue(view1.containsEdge((EdgeImpl) e), "Edge " + e.getId() + " should be in view after union"); + } } @Test @@ -918,6 +1076,12 @@ public void testIntersectionBothEmpty() { Assert.assertEquals(view1.getNodeCount(), 0, "View1 should still be empty"); Assert.assertEquals(view1.getEdgeCount(), 0, "View1 should still have no edges"); + + // Verify all elements are absent (trivial case but validates correctness) + for (Node n : graphStore.getNodes()) { + Assert.assertFalse(view1 + .containsNode((NodeImpl) n), "No nodes should be in view after intersection of empty views"); + } } @Test @@ -936,6 +1100,12 @@ public void testUnionBothEmpty() { Assert.assertEquals(view1.getNodeCount(), 0, "View1 should still be empty"); Assert.assertEquals(view1.getEdgeCount(), 0, "View1 should still have no edges"); + + // Verify all elements are absent (trivial case but validates correctness) + for (Node n : graphStore.getNodes()) { + Assert.assertFalse(view1 + .containsNode((NodeImpl) n), "No nodes should be in view after union of empty views"); + } } // ========== Tests for Retain Operations ========== @@ -1032,7 +1202,6 @@ public void testRetainNodesWithMutualEdges() { view.fill(); EdgeImpl e0 = graphStore.getEdge("0"); - EdgeImpl e1 = graphStore.getEdge("1"); NodeImpl n1 = e0.getSource(); NodeImpl n2 = e0.getTarget(); From 6a67d9040759f5c51e1ce06c0ac924e736148a37 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 28 Oct 2025 15:33:24 +0100 Subject: [PATCH 206/271] Add createView method to GraphModel based on predicates (#259) * Add create view based on parallel streams * Complete adding view creation based on predicates --- .../java/org/gephi/graph/api/GraphModel.java | 25 ++- .../org/gephi/graph/impl/GraphModelImpl.java | 6 + .../gephi/graph/impl/GraphViewDecorator.java | 3 + .../org/gephi/graph/impl/GraphViewImpl.java | 56 ++++- .../org/gephi/graph/impl/GraphViewStore.java | 60 +++--- .../java/org/gephi/graph/impl/NodeStore.java | 3 - .../org/gephi/graph/impl/GraphModelTest.java | 197 ++++++++++++++++++ .../gephi/graph/impl/GraphViewImplTest.java | 33 +++ 8 files changed, 337 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index 64383bbd..1d4ab30b 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -19,6 +19,7 @@ import java.io.DataOutput; import java.io.IOException; import java.time.ZoneId; +import java.util.function.Predicate; import org.gephi.graph.impl.GraphModelImpl; /** @@ -488,16 +489,38 @@ public static interface DefaultColumns { /** * Creates a new graph view. + *

+ * By default, the view applies to both nodes and edges, so this is equivalent + * to {@link #createView(boolean, boolean) createView(true, true)}. + *

+ * New views are by default empty, i.e. no nodes and no edges are visible in the + * view. * * @return newly created graph view */ public GraphView createView(); + /** + * Creates a new graph view. + *

+ * The node and edge filters allows to restrict the view filtering to only nodes + * or only edges. If node only, all edges connected to included nodes will be + * included too. If edge only, all nodes are included but only the edges + * matching the view are included. + * + * @param nodeFilter predicate to filter nodes, or null to include all nodes + * @param edgeFilter predicate to filter edges, or null to include all edges + * @return newly created graph view + */ + public GraphView createView(Predicate nodeFilter, Predicate edgeFilter); + /** * Creates a new graph view. *

* The node and edge parameters allows to restrict the view filtering to only - * nodes or only edges. By default, the view applies to both nodes and edges. + * nodes or only edges. If node only, all edges connected to included nodes will + * be included too. If edge only, all nodes are included but only the edges + * matching the view are included. * * @param node true to enable node view, false otherwise * @param edge true to enable edge view, false otherwise diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 062fba91..587359f0 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -17,6 +17,7 @@ import java.time.ZoneId; import java.util.Arrays; +import java.util.function.Predicate; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.DirectedGraph; import org.gephi.graph.api.DirectedSubgraph; @@ -251,6 +252,11 @@ public GraphView createView() { return store.viewStore.createView(); } + @Override + public GraphView createView(Predicate nodeFilter, Predicate edgeFilter) { + return store.viewStore.createView(nodeFilter, edgeFilter); + } + @Override public GraphView createView(boolean node, boolean edge) { return store.viewStore.createView(node, edge); diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 3a7b55e4..062cd69b 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -385,6 +385,9 @@ public boolean hasEdge(final Object id) { @Override public NodeIterable getNodes() { + if (!view.isNodeView()) { + return graphStore.getNodes(); + } return new NodeIterableWrapper(() -> new NodeViewIterator(graphStore.nodeStore.iterator()), NodeViewSpliterator::new, graphStore.getAutoLock()); } diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index ae02fb75..0eece38f 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -22,6 +22,7 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.function.Predicate; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; @@ -77,6 +78,53 @@ public GraphViewImpl(final GraphStore store, boolean nodes, boolean edges) { this.interval = Interval.INFINITY_INTERVAL; } + public GraphViewImpl(final GraphStore store, Predicate nodePredicate, Predicate edgePredicate) { + this(store, nodePredicate != null, edgePredicate != null); + + // Fill - optimized with iterators and manual counting + if (nodePredicate != null) { + int count = 0; + for (Node node : graphStore.nodeStore) { + if (nodePredicate.test(node)) { + nodeBitVector.set(node.getStoreId()); + count++; + } + } + nodeCount = count; + incrementNodeVersion(); + } + + // Process edges with iterator + int count = 0; + for (Edge edge : graphStore.edgeStore) { + // Cache store IDs + int sourceId = edge.getSource().getStoreId(); + int targetId = edge.getTarget().getStoreId(); + + // Filter by node predicate if needed + if (nodePredicate != null && (!nodeBitVector.get(sourceId) || !nodeBitVector.get(targetId))) { + continue; + } + + // Filter by edge predicate if needed + if (edgePredicate != null && !edgePredicate.test(edge)) { + continue; + } + + edgeBitVector.set(edge.getStoreId()); + int type = edge.getType(); + typeCounts[type]++; + count++; + + if (((EdgeImpl) edge).isMutual() && !edge.isSelfLoop() && containsEdge(graphStore.edgeStore + .get(edge.getTarget(), edge.getSource(), type, false))) { + mutualEdgeTypeCounts[type]++; + mutualEdgesCount++; + } + } + edgeCount = count; + } + public GraphViewImpl(final GraphViewImpl view, boolean nodes, boolean edges) { this.graphStore = view.graphStore; this.nodeView = nodes; @@ -962,14 +1010,6 @@ protected void destroyAllObservers() { } } - protected void ensureNodeVectorSize(NodeImpl node) { - // BitSet automatically grows as needed, no manual resizing required - } - - protected void ensureEdgeVectorSize(EdgeImpl edge) { - // BitSet automatically grows as needed, no manual resizing required - } - protected void setEdgeType(EdgeImpl edgeImpl, int oldType, boolean wasMutual) { ensureTypeCountArrayCapacity(edgeImpl.type); typeCounts[oldType]--; diff --git a/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 17e8e2d2..01b86158 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -17,6 +17,7 @@ import it.unimi.dsi.fastutil.ints.IntRBTreeSet; import it.unimi.dsi.fastutil.ints.IntSortedSet; +import java.util.function.Predicate; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; @@ -54,6 +55,17 @@ public GraphViewImpl createView() { return createView(true, true); } + public GraphViewImpl createView(Predicate nodeFilter, Predicate edgeFilter) { + graphStore.autoWriteLock(); + try { + GraphViewImpl graphView = new GraphViewImpl(graphStore, nodeFilter, edgeFilter); + addView(graphView); + return graphView; + } finally { + graphStore.autoWriteUnlock(); + } + } + public GraphViewImpl createView(boolean nodes, boolean edges) { graphStore.autoWriteLock(); try { @@ -232,58 +244,38 @@ public void destroyGraphObserver(GraphObserverImpl graphObserver) { graphViewImpl.destroyGraphObserver(graphObserver); } - protected void addNode(NodeImpl node) { - if (views.length > 0) { - for (GraphViewImpl view : views) { - if (view != null) { - view.ensureNodeVectorSize(node); - } - } - } - } - protected void removeNode(NodeImpl node) { - if (views.length > 0) { - for (GraphViewImpl view : views) { - if (view != null) { - view.removeNode(node); - } + for (GraphViewImpl view : views) { + if (view != null) { + view.removeNode(node); } } } protected void addEdge(EdgeImpl edge) { - if (views.length > 0) { - for (GraphViewImpl view : views) { - if (view != null) { - view.ensureEdgeVectorSize(edge); - - if (view.nodeView && !view.edgeView) { - view.addEdgeInNodeView(edge); - } + for (GraphViewImpl view : views) { + if (view != null) { + if (view.nodeView && !view.edgeView) { + view.addEdgeInNodeView(edge); } } } } protected void setEdgeType(EdgeImpl edge, int oldType, boolean wasMutual) { - if (views.length > 0) { - for (GraphViewImpl view : views) { - if (view != null) { - if ((view.nodeView && !view.edgeView) || (view.edgeView && view.containsEdge(edge))) { - view.setEdgeType(edge, oldType, wasMutual); - } + for (GraphViewImpl view : views) { + if (view != null) { + if ((view.nodeView && !view.edgeView) || (view.edgeView && view.containsEdge(edge))) { + view.setEdgeType(edge, oldType, wasMutual); } } } } protected void removeEdge(EdgeImpl edge) { - if (views.length > 0) { - for (GraphViewImpl view : views) { - if (view != null) { - view.removeEdge(edge); - } + for (GraphViewImpl view : views) { + if (view != null) { + view.removeEdge(edge); } } } diff --git a/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java index 14e43744..08fa1d68 100644 --- a/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -305,9 +305,6 @@ public boolean add(final Node n) { currentBlock.add(node); dictionary.put(node.getId(), node.storeId); } - if (viewStore != null) { - viewStore.addNode(node); - } node.indexAttributes(); if (spatialIndex != null) { diff --git a/src/test/java/org/gephi/graph/impl/GraphModelTest.java b/src/test/java/org/gephi/graph/impl/GraphModelTest.java index 07125d01..12a95e1a 100644 --- a/src/test/java/org/gephi/graph/impl/GraphModelTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphModelTest.java @@ -202,6 +202,203 @@ public void testCreateViewCustom() { Assert.assertFalse(view.isEdgeView()); } + @Test + public void testCreateViewWithPredicates() { + GraphModelImpl graphModel = new GraphModelImpl(); + GraphView view = graphModel.createView(n -> true, e -> true); + Assert.assertNotNull(view); + Assert.assertSame(view.getGraphModel(), graphModel); + Assert.assertTrue(view.isNodeView()); + Assert.assertTrue(view.isEdgeView()); + } + + @Test + public void testCreateViewWithNodePredicateOnly() { + GraphModelImpl graphModel = new GraphModelImpl(); + GraphView view = graphModel.createView(n -> true, null); + Assert.assertNotNull(view); + Assert.assertTrue(view.isNodeView()); + Assert.assertFalse(view.isEdgeView()); + } + + @Test + public void testCreateViewWithEdgePredicateOnly() { + GraphModelImpl graphModel = new GraphModelImpl(); + GraphView view = graphModel.createView(null, e -> true); + Assert.assertNotNull(view); + Assert.assertFalse(view.isNodeView()); + Assert.assertTrue(view.isEdgeView()); + } + + @Test + public void testCreateViewWithBothPredicatesNull() { + GraphModelImpl graphModel = new GraphModelImpl(); + GraphView view = graphModel.createView(null, null); + Assert.assertNotNull(view); + Assert.assertFalse(view.isNodeView()); + Assert.assertFalse(view.isEdgeView()); + } + + @Test + public void testCreateViewWithNodePredicateFiltering() { + GraphModelImpl graphModel = new GraphModelImpl(); + Table table = graphModel.getNodeTable(); + Column col = table.addColumn("value", Integer.class); + + Node n1 = graphModel.factory().newNode("1"); + n1.setAttribute(col, 10); + Node n2 = graphModel.factory().newNode("2"); + n2.setAttribute(col, 20); + Node n3 = graphModel.factory().newNode("3"); + n3.setAttribute(col, 30); + graphModel.getStore().addAllNodes(Arrays.asList(new Node[] { n1, n2, n3 })); + + // Create view with predicate that filters nodes with value > 15 + GraphView view = graphModel.createView(n -> { + Integer value = (Integer) n.getAttribute(col); + return value != null && value > 15; + }, null); + + Graph subgraph = graphModel.getGraph(view); + Assert.assertEquals(subgraph.getNodeCount(), 2); + Assert.assertTrue(subgraph.contains(n2)); + Assert.assertTrue(subgraph.contains(n3)); + Assert.assertFalse(subgraph.contains(n1)); + } + + @Test + public void testCreateViewWithEdgePredicateFiltering() { + GraphModelImpl graphModel = new GraphModelImpl(); + Table table = graphModel.getEdgeTable(); + Column col = table.addColumn("weight_custom", Double.class); + + Node n1 = graphModel.factory().newNode("1"); + Node n2 = graphModel.factory().newNode("2"); + Node n3 = graphModel.factory().newNode("3"); + graphModel.getStore().addAllNodes(Arrays.asList(new Node[] { n1, n2, n3 })); + + Edge e1 = graphModel.factory().newEdge(n1, n2); + e1.setAttribute(col, 1.0); + Edge e2 = graphModel.factory().newEdge(n2, n3); + e2.setAttribute(col, 5.0); + Edge e3 = graphModel.factory().newEdge(n1, n3); + e3.setAttribute(col, 10.0); + graphModel.getStore().addAllEdges(Arrays.asList(new Edge[] { e1, e2, e3 })); + + // Create view with predicate that filters edges with weight >= 5.0 + GraphView view = graphModel.createView(null, e -> { + Double weight = (Double) e.getAttribute(col); + return weight != null && weight >= 5.0; + }); + + Graph subgraph = graphModel.getGraph(view); + Assert.assertEquals(subgraph.getNodeCount(), 3); // All nodes included + Assert.assertEquals(subgraph.getEdgeCount(), 2); + Assert.assertTrue(subgraph.contains(e2)); + Assert.assertTrue(subgraph.contains(e3)); + Assert.assertFalse(subgraph.contains(e1)); + } + + @Test + public void testCreateViewWithBothPredicatesFiltering() { + GraphModelImpl graphModel = new GraphModelImpl(); + Table nodeTable = graphModel.getNodeTable(); + Table edgeTable = graphModel.getEdgeTable(); + Column nodeCol = nodeTable.addColumn("active", Boolean.class); + Column edgeCol = edgeTable.addColumn("strength", Double.class); + + Node n1 = graphModel.factory().newNode("1"); + n1.setAttribute(nodeCol, true); + Node n2 = graphModel.factory().newNode("2"); + n2.setAttribute(nodeCol, false); + Node n3 = graphModel.factory().newNode("3"); + n3.setAttribute(nodeCol, true); + graphModel.getStore().addAllNodes(Arrays.asList(new Node[] { n1, n2, n3 })); + + Edge e1 = graphModel.factory().newEdge(n1, n2); + e1.setAttribute(edgeCol, 0.5); + Edge e2 = graphModel.factory().newEdge(n2, n3); + e2.setAttribute(edgeCol, 0.8); + Edge e3 = graphModel.factory().newEdge(n1, n3); + e3.setAttribute(edgeCol, 0.3); + graphModel.getStore().addAllEdges(Arrays.asList(new Edge[] { e1, e2, e3 })); + + // Create view with both predicates + GraphView view = graphModel.createView(n -> Boolean.TRUE.equals(n.getAttribute(nodeCol)), e -> { + Double strength = (Double) e.getAttribute(edgeCol); + return strength != null && strength > 0.4; + }); + + Graph subgraph = graphModel.getGraph(view); + + Assert.assertEquals(subgraph.getNodeCount(), 2); // n1 and n3 + Assert.assertTrue(subgraph.contains(n1)); + Assert.assertTrue(subgraph.contains(n3)); + Assert.assertFalse(subgraph.contains(n2)); + + // No edges should be included: + // - e1 has n2 which is not in node view (active=false) + // - e2 has n2 which is not in node view (active=false) + // - e3 connects n1 and n3 (both in view) but strength 0.3 fails edge filter + Assert.assertEquals(subgraph.getEdgeCount(), 0); + Assert.assertFalse(subgraph.contains(e1)); + Assert.assertFalse(subgraph.contains(e2)); + Assert.assertFalse(subgraph.contains(e3)); + } + + @Test + public void testCreateViewWithBothPredicatesFilteringWithMatchingEdges() { + GraphModelImpl graphModel = new GraphModelImpl(); + Table nodeTable = graphModel.getNodeTable(); + Table edgeTable = graphModel.getEdgeTable(); + Column nodeCol = nodeTable.addColumn("category", String.class); + Column edgeCol = edgeTable.addColumn("score", Double.class); + + Node n1 = graphModel.factory().newNode("1"); + n1.setAttribute(nodeCol, "A"); + Node n2 = graphModel.factory().newNode("2"); + n2.setAttribute(nodeCol, "B"); + Node n3 = graphModel.factory().newNode("3"); + n3.setAttribute(nodeCol, "A"); + Node n4 = graphModel.factory().newNode("4"); + n4.setAttribute(nodeCol, "A"); + graphModel.getStore().addAllNodes(Arrays.asList(new Node[] { n1, n2, n3, n4 })); + + Edge e1 = graphModel.factory().newEdge(n1, n2); + e1.setAttribute(edgeCol, 5.0); + Edge e2 = graphModel.factory().newEdge(n1, n3); + e2.setAttribute(edgeCol, 3.0); + Edge e3 = graphModel.factory().newEdge(n3, n4); + e3.setAttribute(edgeCol, 8.0); + Edge e4 = graphModel.factory().newEdge(n2, n4); + e4.setAttribute(edgeCol, 1.0); + graphModel.getStore().addAllEdges(Arrays.asList(new Edge[] { e1, e2, e3, e4 })); + + // Create view: nodes with category "A" AND edges with score > 2.0 + GraphView view = graphModel.createView(n -> "A".equals(n.getAttribute(nodeCol)), e -> { + Double score = (Double) e.getAttribute(edgeCol); + return score != null && score > 2.0; + }); + + Graph subgraph = graphModel.getGraph(view); + + // Nodes: n1, n3, n4 (all category "A") + Assert.assertEquals(subgraph.getNodeCount(), 3); + Assert.assertTrue(subgraph.contains(n1)); + Assert.assertTrue(subgraph.contains(n3)); + Assert.assertTrue(subgraph.contains(n4)); + Assert.assertFalse(subgraph.contains(n2)); + + // Edges: only e2 (n1-n3, score 3.0) and e3 (n3-n4, score 8.0) + // e1 excluded: n2 not in view + // e4 excluded: n2 not in view + Assert.assertEquals(subgraph.getEdgeCount(), 2); + Assert.assertFalse(subgraph.contains(e1)); + Assert.assertTrue(subgraph.contains(e2)); + Assert.assertTrue(subgraph.contains(e3)); + Assert.assertFalse(subgraph.contains(e4)); + } + @Test public void testCopyView() { GraphModelImpl graphModel = new GraphModelImpl(); diff --git a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java index 377d3a6c..5092c72c 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java @@ -25,6 +25,7 @@ import org.gephi.graph.api.GraphFactory; import org.gephi.graph.api.GraphView; import org.gephi.graph.api.Node; +import org.gephi.graph.api.Subgraph; import org.gephi.graph.api.UndirectedSubgraph; import org.testng.Assert; import org.testng.annotations.Test; @@ -48,6 +49,7 @@ public void testFill() { } for (Node n : graphStore.getNodes()) { Assert.assertTrue(graph.contains(n)); + Assert.assertEquals(graph.getDegree(n), graphStore.getDegree(n)); } for (int i = 0; i < graphStore.edgeTypeStore.length; i++) { Assert.assertEquals(graph.getEdgeCount(i), graphStore.getEdgeCount(i)); @@ -108,6 +110,20 @@ public void testAddEdgeMainView() { Assert.assertTrue(view.containsEdge(edge)); } + @Test + public void testEdgeViewNodeBehaviors() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(false, true); + Subgraph subgraph = store.getGraph(view); + Assert.assertEquals(subgraph.getNodeCount(), graphStore.getNodeCount()); + Assert.assertEquals(subgraph.getNodes().stream().count(), graphStore.getNodeCount()); + for (Node node : subgraph.getNodes()) { + Assert.assertSame(subgraph.getNode(node.getId()), node); + Assert.assertEquals(subgraph.getDegree(node), 0); + } + } + @Test public void testViewDeepEquals() { GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); @@ -524,6 +540,23 @@ public void testNodeViewEdgeUpdate() { Assert.assertTrue(view.containsEdge(edge)); } + @Test + public void testEdgeViewUpdate() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + GraphViewImpl view = store.createView(false, true); + + GraphFactory factory = graphStore.factory; + Node n1 = factory.newNode("foo1"); + Node n2 = factory.newNode("foo2"); + graphStore.addNode(n1); + graphStore.addNode(n2); + Assert.assertEquals(view.getNodeCount(), graphStore.getNodeCount()); + Edge e1 = factory.newEdge("foo", n1, n2, 0, 0.0, true); + graphStore.addEdge(e1); + Assert.assertFalse(view.containsEdge(e1)); + } + @Test public void testIsNodeView() { GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); From 3f48d943564c6bbd180ff2baa5d38fd4312b19fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:35:55 +0100 Subject: [PATCH 207/271] Bump it.unimi.dsi:fastutil from 8.5.16 to 8.5.18 (#249) Bumps [it.unimi.dsi:fastutil](https://github.com/vigna/fastutil) from 8.5.16 to 8.5.18. - [Changelog](https://github.com/vigna/fastutil/blob/master/CHANGES) - [Commits](https://github.com/vigna/fastutil/commits) --- updated-dependencies: - dependency-name: it.unimi.dsi:fastutil dependency-version: 8.5.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 14e2c0da..eca63d85 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ it.unimi.dsi fastutil - 8.5.16 + 8.5.18 From fcc2af160df2f8558eb0caf5bd0345a79d45a329 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:36:07 +0100 Subject: [PATCH 208/271] Bump org.apache.maven.plugins:maven-compiler-plugin (#250) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.14.0 to 3.14.1. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.14.0...maven-compiler-plugin-3.14.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.14.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index eca63d85..56eda1df 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.0 + 3.14.1 org.apache.maven.plugins From 58850247c30a8954830e3cbfd0a7e790b26c8a4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:36:16 +0100 Subject: [PATCH 209/271] Bump org.jacoco:jacoco-maven-plugin from 0.8.13 to 0.8.14 (#256) Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.13 to 0.8.14. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.13...v0.8.14) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-version: 0.8.14 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 56eda1df..cf5d05b4 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.jacoco jacoco-maven-plugin - 0.8.13 + 0.8.14 org.eluder.coveralls From 3d1d9ae87cb818ca1e312d2f7eb3ccf20c29dd9a Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 28 Oct 2025 15:37:16 +0100 Subject: [PATCH 210/271] Set version to 0.8.1 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cf5d05b4..9723e982 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.1-SNAPSHOT + 0.8.1 jar GraphStore From e6e54d8762d21286fa1f0e8824eb1a84ef84534b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 28 Oct 2025 16:10:47 +0100 Subject: [PATCH 211/271] Set version to 0.8.2-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9723e982..97a619f6 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.1 + 0.8.2-SNAPSHOT jar GraphStore From a9f77a641ab8ff58a56b3f23d514a5922bec7864 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 28 Oct 2025 16:58:54 +0100 Subject: [PATCH 212/271] Update Gephi graphstore dependency version to 0.8.1 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5652e64b..7b83c5a5 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.8.0 + 0.8.1 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.8.0' +compile 'org.gephi:graphstore:0.8.1' ``` ## Dependencies From 78add2710ac6d335e8a8196f57886c5d0ad05be2 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 28 Oct 2025 20:09:39 +0100 Subject: [PATCH 213/271] Fix flaky test --- .../java/org/gephi/graph/impl/NodesQuadTreeTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java b/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java index 9f12091b..dc48e002 100644 --- a/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java +++ b/src/test/java/org/gephi/graph/impl/NodesQuadTreeTest.java @@ -128,7 +128,7 @@ public void testDepthZero() { @Test public void testDepth() { NodesQuadTree q = new NodesQuadTree(BOUNDS_RECT); - Random random = new Random(); + Random random = new Random(42L); for (int i = 0; i <= GraphStoreConfiguration.SPATIAL_INDEX_MAX_OBJECTS_PER_NODE; i++) { NodeImpl node = new NodeImpl(String.valueOf(i)); node.setPosition(random.nextInt((int) BOUNDS * 2) - BOUNDS, random.nextInt((int) BOUNDS * 2) - BOUNDS); @@ -791,7 +791,7 @@ private NodeImpl[] addRandomNodes(NodesQuadTree q, int count, int startIndex, Re } private NodeImpl[] generateNodes(int count, int startIndex, Rect2D area) { - Random rand = new Random(); + Random rand = new Random(42L); NodeImpl[] nodes = new NodeImpl[count]; for (int i = 0; i < count; i++) { NodeImpl node = new NodeImpl(String.valueOf(startIndex++)); @@ -805,14 +805,15 @@ private NodeImpl[] generateNodes(int count, int startIndex, Rect2D area) { } private EdgeImpl[] addRandomEdges(GraphStore store, NodeImpl[] nodes, int count) { + Random rand = new Random(789012L); for (NodeImpl n : nodes) { store.addNode(n); } EdgeImpl[] edges = new EdgeImpl[count]; int edgeIndex = 0; while (edgeIndex < count) { - NodeImpl source = nodes[new Random().nextInt(nodes.length)]; - NodeImpl target = nodes[new Random().nextInt(nodes.length)]; + NodeImpl source = nodes[rand.nextInt(nodes.length)]; + NodeImpl target = nodes[rand.nextInt(nodes.length)]; if (source != target) { EdgeImpl edge = new EdgeImpl(String.valueOf(edgeIndex), store, source, target, 0, 1.0, true); edges[edgeIndex] = edge; From cff33ea6fdf1641fe1a3fc5ee00a6a750127e2a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 17:57:13 +0100 Subject: [PATCH 214/271] Bump actions/checkout from 5 to 6 (#261) Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a138aa66..fd07f9ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up Maven Central Repository uses: actions/setup-java@v5 with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index b11ee6c7..a1ffefbf 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -7,7 +7,7 @@ jobs: build_and_test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Set up JDK 17 uses: actions/setup-java@v5 with: From ee88a04fca2ddcc5086ba1b998ee50e9fdf1416e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:52:28 +0100 Subject: [PATCH 215/271] Bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.4 to 3.5.5 (#266) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.5.4 to 3.5.5. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.4...surefire-3.5.5) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 97a619f6..fafb4451 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 + 3.5.5 org.apache.maven.plugins From 5a4dd65419d6e6f839a51dba7ae66e3ad298c576 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:52:45 +0100 Subject: [PATCH 216/271] Bump org.apache.maven.plugins:maven-compiler-plugin (#265) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.14.1 to 3.15.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.14.1...maven-compiler-plugin-3.15.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-version: 3.15.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fafb4451..a8423b13 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.1 + 3.15.0 org.apache.maven.plugins From 3cc198ff4f36c532a1afad3ac01c70dd47b08740 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:52:58 +0100 Subject: [PATCH 217/271] Bump org.testng:testng from 7.11.0 to 7.12.0 (#264) Bumps [org.testng:testng](https://github.com/testng-team/testng) from 7.11.0 to 7.12.0. - [Release notes](https://github.com/testng-team/testng/releases) - [Changelog](https://github.com/testng-team/testng/blob/master/CHANGES.txt) - [Commits](https://github.com/testng-team/testng/compare/7.11.0...7.12.0) --- updated-dependencies: - dependency-name: org.testng:testng dependency-version: 7.12.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a8423b13..0985742e 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ org.testng testng - 7.11.0 + 7.12.0 test From 9fd4aec78141a731b349629102156ed4e9c2ba58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:54:10 +0100 Subject: [PATCH 218/271] Bump org.sonatype.central:central-publishing-maven-plugin (#263) Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.9.0 to 0.10.0. - [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits) --- updated-dependencies: - dependency-name: org.sonatype.central:central-publishing-maven-plugin dependency-version: 0.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0985742e..e5516d59 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ org.sonatype.central central-publishing-maven-plugin - 0.9.0 + 0.10.0 true From 0e23b403594bc98695841ae7498d32b40c65e410 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:54:26 +0100 Subject: [PATCH 219/271] Bump org.apache.maven.plugins:maven-source-plugin from 3.3.1 to 3.4.0 (#262) Bumps [org.apache.maven.plugins:maven-source-plugin](https://github.com/apache/maven-source-plugin) from 3.3.1 to 3.4.0. - [Release notes](https://github.com/apache/maven-source-plugin/releases) - [Commits](https://github.com/apache/maven-source-plugin/compare/maven-source-plugin-3.3.1...maven-source-plugin-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-source-plugin dependency-version: 3.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e5516d59..3dff289a 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.1 + 3.4.0 org.apache.maven.plugins From bd8fe1ba1c1038a4ff961f05cf2f21bf1945bffb Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 15 Mar 2026 10:35:18 +0100 Subject: [PATCH 220/271] Add test for default alpha --- .../org/gephi/graph/impl/ElementPropertiesTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/java/org/gephi/graph/impl/ElementPropertiesTest.java b/src/test/java/org/gephi/graph/impl/ElementPropertiesTest.java index 0f3fd79a..b04cf8ea 100644 --- a/src/test/java/org/gephi/graph/impl/ElementPropertiesTest.java +++ b/src/test/java/org/gephi/graph/impl/ElementPropertiesTest.java @@ -26,6 +26,17 @@ */ public class ElementPropertiesTest { + @Test + public void testDefaultsAlpha() { + NodeImpl.NodePropertiesImpl p = new NodeImpl.NodePropertiesImpl(); + Assert.assertEquals(p.alpha(), 1f); + Assert.assertEquals(p.getTextProperties().getAlpha(), 1f); + + EdgeImpl.EdgePropertiesImpl ep = new EdgeImpl.EdgePropertiesImpl(); + Assert.assertEquals(ep.alpha(), 1f); + Assert.assertEquals(ep.getTextProperties().getAlpha(), 1f); + } + @Test public void testNodeProperties() { NodeImpl.NodePropertiesImpl p = new NodeImpl.NodePropertiesImpl(); From 14db8c275adbcf1e5884f9113c7ee6e7d9092ce6 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 15 Mar 2026 10:39:40 +0100 Subject: [PATCH 221/271] Overwrite zero alpha to support opening older files --- .../java/org/gephi/graph/impl/Serialization.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index efe44c19..1fbe5e36 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -809,6 +809,12 @@ private EdgePropertiesImpl deserializeEdgeProperties(final DataInput is) throws props.rgba = rgba; props.setTextProperties(textProperties); + // Gephi versions before 0.11 used zero alpha to indicate that the element has no color + // Override this to avoid hidden elements + if (props.alpha() == 0f) { + props.setAlpha(1f); + } + return props; } @@ -837,6 +843,12 @@ private TextPropertiesImpl deserializeTextProperties(final DataInput is) throws props.width = width; props.height = height; + // Gephi versions before 0.11 used zero alpha to indicate that the element has no color + // Override this to avoid hidden elements + if (props.getAlpha() == 0f) { + props.setAlpha(1f); + } + return props; } From 756160aadf7fd68ecaa5ed60ad58b283bdbb64fe Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 15 Mar 2026 10:44:01 +0100 Subject: [PATCH 222/271] Correct zero alpha in case it's present #267 --- src/main/java/org/gephi/graph/impl/Serialization.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 1fbe5e36..6ca2ac6c 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -811,7 +811,7 @@ private EdgePropertiesImpl deserializeEdgeProperties(final DataInput is) throws // Gephi versions before 0.11 used zero alpha to indicate that the element has no color // Override this to avoid hidden elements - if (props.alpha() == 0f) { + if (props.alpha() <= 0f) { props.setAlpha(1f); } @@ -845,7 +845,7 @@ private TextPropertiesImpl deserializeTextProperties(final DataInput is) throws // Gephi versions before 0.11 used zero alpha to indicate that the element has no color // Override this to avoid hidden elements - if (props.getAlpha() == 0f) { + if (props.getAlpha() <= 0f) { props.setAlpha(1f); } From 9c4b29f8d4b6e0a74c91dfc941a7bbf4d9f384fc Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 15 Mar 2026 10:46:56 +0100 Subject: [PATCH 223/271] Fix tests --- src/test/java/org/gephi/graph/impl/SerializationTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 561a28c6..a14ac49c 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -344,7 +344,7 @@ public void testGraphVersion() throws IOException, ClassNotFoundException { @Test public void testTextProperties() throws IOException, ClassNotFoundException { TextPropertiesImpl textProperties = new TextPropertiesImpl(); - textProperties.rgba = 100; + textProperties.rgba = 0x01FF0000; textProperties.size = 3f; textProperties.text = "foo"; textProperties.visible = true; @@ -366,7 +366,7 @@ public void testNodeProperties() throws IOException, ClassNotFoundException { nodeProperties.rgba = 100; nodeProperties.size = 4f; nodeProperties.fixed = true; - nodeProperties.textProperties.rgba = 200; + nodeProperties.textProperties.rgba = 0x01FF0000; nodeProperties.textProperties.size = 5f; nodeProperties.textProperties.text = "foo"; nodeProperties.textProperties.visible = true; @@ -380,8 +380,8 @@ public void testNodeProperties() throws IOException, ClassNotFoundException { @Test public void testEdgeProperties() throws IOException, ClassNotFoundException { EdgeImpl.EdgePropertiesImpl edgeProperties = new EdgeImpl.EdgePropertiesImpl(); - edgeProperties.rgba = 100; - edgeProperties.textProperties.rgba = 200; + edgeProperties.rgba = 0x01FF0000; + edgeProperties.textProperties.rgba = 0x01FF0000; edgeProperties.textProperties.size = 5f; edgeProperties.textProperties.text = "foo"; edgeProperties.textProperties.visible = true; From 926c60a1793ac9df7f1bb7e9c7e391f5905d49a9 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 15 Mar 2026 10:48:59 +0100 Subject: [PATCH 224/271] Set version to 0.8.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3dff289a..4cd62da3 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.2-SNAPSHOT + 0.8.2 jar GraphStore From 4151fb09a60a405b7b62cb826c02704bde0a06c3 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 15 Mar 2026 10:52:36 +0100 Subject: [PATCH 225/271] Set version to 0.8.3-SNAPSHOT --- README.md | 4 ++-- pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7b83c5a5..13f1c737 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.8.1 + 0.8.2 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.8.1' +compile 'org.gephi:graphstore:0.8.2' ``` ## Dependencies diff --git a/pom.xml b/pom.xml index 4cd62da3..827d2835 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.2 + 0.8.3-SNAPSHOT jar GraphStore From 06f551de723ea11ff3f29017fb26e88cdde7ea38 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 28 Mar 2026 08:05:48 +0100 Subject: [PATCH 226/271] Implement ##268 Expose Edge.isMutual() --- src/main/java/org/gephi/graph/api/Edge.java | 8 ++++++++ src/main/java/org/gephi/graph/impl/EdgeImpl.java | 3 ++- src/test/java/org/gephi/graph/impl/BasicGraphStore.java | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/gephi/graph/api/Edge.java b/src/main/java/org/gephi/graph/api/Edge.java index f269be0d..79bb8ab0 100644 --- a/src/main/java/org/gephi/graph/api/Edge.java +++ b/src/main/java/org/gephi/graph/api/Edge.java @@ -141,4 +141,12 @@ public interface Edge extends Element, EdgeProperties { * @return true if directed, false otherwise */ public boolean isDirected(); + + /** + * Returns true if this edge is directed and another edge exists in the opposite + * direction. + * + * @return true if mutual, false otherwise + */ + public boolean isMutual(); } diff --git a/src/main/java/org/gephi/graph/impl/EdgeImpl.java b/src/main/java/org/gephi/graph/impl/EdgeImpl.java index d469aaa6..c511f0be 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeImpl.java +++ b/src/main/java/org/gephi/graph/impl/EdgeImpl.java @@ -215,7 +215,8 @@ protected void setMutual(boolean mutual) { } } - protected boolean isMutual() { + @Override + public boolean isMutual() { return (flags & MUTUAL_BYTE) == MUTUAL_BYTE; } diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index 6e17f62b..19cd34c1 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -1012,6 +1012,11 @@ public boolean isDirected() { return directed; } + @Override + public boolean isMutual() { + return false; + } + public String getStringId() { return BasicEdgeStore.getStringId(source, target, directed); } From 303b939cceb82150cf6256d7d69d71bf6eb1533b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 28 Mar 2026 08:11:49 +0100 Subject: [PATCH 227/271] Set version to 0.8.3 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 827d2835..2db1c659 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.3-SNAPSHOT + 0.8.3 jar GraphStore From 664a4332bfd0acb77c01287172952cf1d712d942 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 28 Mar 2026 08:14:16 +0100 Subject: [PATCH 228/271] Set version to 0.8.4-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2db1c659..cc2f7e8e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.3 + 0.8.4-SNAPSHOT jar GraphStore From 4eb5f5893f1d490f89dbbe80129ccd80c013c4b8 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 28 Mar 2026 08:15:08 +0100 Subject: [PATCH 229/271] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 13f1c737..af51d20f 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.8.2 + 0.8.3 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.8.2' +compile 'org.gephi:graphstore:0.8.3' ``` ## Dependencies From 381e55ec8a61cf48e7d5cd7aa05d7db474f06fb2 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 12 Apr 2026 14:58:18 +0200 Subject: [PATCH 230/271] Fix issue ##269 --- .../org/gephi/graph/api/NodeProperties.java | 6 +++++ .../java/org/gephi/graph/impl/NodeImpl.java | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/NodeProperties.java b/src/main/java/org/gephi/graph/api/NodeProperties.java index 7d7b691e..5f07c9e8 100644 --- a/src/main/java/org/gephi/graph/api/NodeProperties.java +++ b/src/main/java/org/gephi/graph/api/NodeProperties.java @@ -69,6 +69,7 @@ public interface NodeProperties extends ElementProperties { * Sets the x position. * * @param x the x position + * @throws IllegalArgumentException if x is NaN */ public void setX(float x); @@ -76,6 +77,7 @@ public interface NodeProperties extends ElementProperties { * Sets the y position. * * @param y the y position + * @throws IllegalArgumentException if y is NaN */ public void setY(float y); @@ -83,6 +85,7 @@ public interface NodeProperties extends ElementProperties { * Sets the z position. * * @param z the z position + * @throws IllegalArgumentException if z is NaN */ public void setZ(float z); @@ -90,6 +93,7 @@ public interface NodeProperties extends ElementProperties { * Sets the size. * * @param size the size + * @throws IllegalArgumentException if size is NaN */ public void setSize(float size); @@ -98,6 +102,7 @@ public interface NodeProperties extends ElementProperties { * * @param x the x position * @param y the y position + * @throws IllegalArgumentException if x or y is NaN */ public void setPosition(float x, float y); @@ -107,6 +112,7 @@ public interface NodeProperties extends ElementProperties { * @param x the x position * @param y the y position * @param z the z position + * @throws IllegalArgumentException if x, y or z is NaN */ public void setPosition(float x, float y, float z); diff --git a/src/main/java/org/gephi/graph/impl/NodeImpl.java b/src/main/java/org/gephi/graph/impl/NodeImpl.java index e3c44777..07d02173 100644 --- a/src/main/java/org/gephi/graph/impl/NodeImpl.java +++ b/src/main/java/org/gephi/graph/impl/NodeImpl.java @@ -201,30 +201,54 @@ protected void setNodeProperties(NodePropertiesImpl nodeProperties) { @Override public void setX(float x) { + if (Float.isNaN(x)) { + throw new IllegalArgumentException("x cannot be NaN"); + } properties.setX(x); updateNodeInSpatialIndex(); } @Override public void setY(float y) { + if (Float.isNaN(y)) { + throw new IllegalArgumentException("y cannot be NaN"); + } properties.setY(y); updateNodeInSpatialIndex(); } @Override public void setZ(float z) { + if (Float.isNaN(z)) { + throw new IllegalArgumentException("z cannot be NaN"); + } properties.setZ(z); updateNodeInSpatialIndex(); } @Override public void setPosition(float x, float y) { + if (Float.isNaN(x)) { + throw new IllegalArgumentException("x cannot be NaN"); + } + if (Float.isNaN(y)) { + throw new IllegalArgumentException("y cannot be NaN"); + } properties.setPosition(x, y); updateNodeInSpatialIndex(); } @Override public void setPosition(float x, float y, float z) { + if (Float.isNaN(x)) { + throw new IllegalArgumentException("x cannot be NaN"); + } + if (Float.isNaN(y)) { + throw new IllegalArgumentException("y cannot be NaN"); + } + if (Float.isNaN(z)) { + throw new IllegalArgumentException("z cannot be NaN"); + } properties.setPosition(x, y, z); updateNodeInSpatialIndex(); } @@ -256,6 +280,9 @@ public void setColor(Color color) { @Override public void setSize(float size) { + if (Float.isNaN(size)) { + throw new IllegalArgumentException("size cannot be NaN"); + } properties.setSize(size); updateNodeInSpatialIndex(); } From 8dfb308ca0b5712f3c12760dbf94980442125b57 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 21 Apr 2026 20:16:26 +0200 Subject: [PATCH 231/271] Enable spatial index by default #270 --- .../java/org/gephi/graph/impl/GraphStoreConfiguration.java | 2 +- src/main/java/org/gephi/graph/impl/Serialization.java | 6 ++++-- src/test/java/org/gephi/graph/impl/NodeImplTest.java | 4 ++-- .../java/org/gephi/graph/impl/SpatialIndexImplTest.java | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java index 3436ad9c..71a640d1 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java +++ b/src/main/java/org/gephi/graph/impl/GraphStoreConfiguration.java @@ -31,7 +31,7 @@ public final class GraphStoreConfiguration { public static final boolean DEFAULT_ENABLE_OBSERVERS = true; public static final boolean DEFAULT_ENABLE_NODE_PROPERTIES = true; public static final boolean DEFAULT_ENABLE_EDGE_PROPERTIES = true; - public static final boolean DEFAULT_ENABLE_SPATIAL_INDEX = false; + public static final boolean DEFAULT_ENABLE_SPATIAL_INDEX = true; public static final boolean DEFAULT_ENABLE_EDGE_WEIGHT_COLUMN = true; public static final boolean DEFAULT_ENABLE_PARALLEL_EDGES_SAME_TYPE = true; // NodeStore diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 6ca2ac6c..8e535440 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -809,7 +809,8 @@ private EdgePropertiesImpl deserializeEdgeProperties(final DataInput is) throws props.rgba = rgba; props.setTextProperties(textProperties); - // Gephi versions before 0.11 used zero alpha to indicate that the element has no color + // Gephi versions before 0.11 used zero alpha to indicate that the element has + // no color // Override this to avoid hidden elements if (props.alpha() <= 0f) { props.setAlpha(1f); @@ -843,7 +844,8 @@ private TextPropertiesImpl deserializeTextProperties(final DataInput is) throws props.width = width; props.height = height; - // Gephi versions before 0.11 used zero alpha to indicate that the element has no color + // Gephi versions before 0.11 used zero alpha to indicate that the element has + // no color // Override this to avoid hidden elements if (props.getAlpha() <= 0f) { props.setAlpha(1f); diff --git a/src/test/java/org/gephi/graph/impl/NodeImplTest.java b/src/test/java/org/gephi/graph/impl/NodeImplTest.java index 6cf17669..b0b4bd6d 100644 --- a/src/test/java/org/gephi/graph/impl/NodeImplTest.java +++ b/src/test/java/org/gephi/graph/impl/NodeImplTest.java @@ -18,8 +18,8 @@ public void testProperties() { @Test(expectedExceptions = NullPointerException.class) public void testPropertiesDisabled() { - GraphStore graphStore = GraphGenerator - .generateTinyGraphStore(Configuration.builder().enableNodeProperties(false).build()); + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(Configuration.builder().enableSpatialIndex(false) + .enableNodeProperties(false).build()); Node n = graphStore.getNode("1"); Assert.assertNull(n.getColor()); } diff --git a/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java b/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java index 8d775726..f2010473 100644 --- a/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java +++ b/src/test/java/org/gephi/graph/impl/SpatialIndexImplTest.java @@ -32,7 +32,7 @@ public class SpatialIndexImplTest { @Test public void testDisabled() { - GraphStore store = GraphGenerator.generateEmptyGraphStore(); + GraphStore store = new GraphStore(null, Configuration.builder().enableSpatialIndex(false).build()); Assert.assertNull(store.spatialIndex); } From 78faab240a6084f1b3bbd033208e8fb445f44e3c Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 21 Apr 2026 20:19:38 +0200 Subject: [PATCH 232/271] Set version to 0.8.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cc2f7e8e..afe59945 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.4-SNAPSHOT + 0.8.4 jar GraphStore From 9915ff6e7e65160d95fc4854ca6c1919e4cf11b4 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 21 Apr 2026 20:27:06 +0200 Subject: [PATCH 233/271] Set version to 0.8.5-SNAPSHOT --- README.md | 4 ++-- pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index af51d20f..d8e638d1 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.8.3 + 0.8.4 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.8.3' +compile 'org.gephi:graphstore:0.8.4' ``` ## Dependencies diff --git a/pom.xml b/pom.xml index afe59945..b6c58d89 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.4 + 0.8.5-SNAPSHOT jar GraphStore From ce7d913ea83c33eff737cb6d927175c262f7d506 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 23 Apr 2026 20:31:05 +0200 Subject: [PATCH 234/271] Address multiple weaknesses on the time index store (#271) --- .../org/gephi/graph/impl/TimeIndexImpl.java | 14 +++++++--- .../org/gephi/graph/impl/TimeIndexStore.java | 21 ++++++++++----- .../java/org/gephi/graph/impl/TimeStore.java | 6 ++--- .../graph/impl/IntervalIndexStoreTest.java | 27 +++++++++++++++++++ .../org/gephi/graph/impl/TimeStoreTest.java | 14 ++++++++++ .../graph/impl/TimestampIndexStoreTest.java | 27 +++++++++++++++++++ 6 files changed, 95 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java index 3e8c50c9..965ede2d 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexImpl.java @@ -48,9 +48,12 @@ public boolean hasElements() { public void clear() { lock(); - timestamps = new TimeIndexEntry[0]; - elementCount = 0; - unlock(); + try { + timestamps = new TimeIndexEntry[0]; + elementCount = 0; + } finally { + unlock(); + } } protected void add(int timestampIndex, Element element) { @@ -72,8 +75,11 @@ protected void add(int timestampIndex, Element element) { protected void remove(int timestampIndex, Element element) { lock(); try { + if (timestampIndex >= timestamps.length) { + return; + } TimeIndexEntry entry = timestamps[timestampIndex]; - if (entry.remove(element)) { + if (entry != null && entry.remove(element)) { elementCount--; if (entry.isEmpty()) { clearEntry(timestampIndex); diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java index 61632f1b..a43bbef8 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java @@ -275,6 +275,9 @@ public TimeIndex getIndex(Graph graph) { if (view.isMainView()) { return mainIndex; } + if (viewIndexes == null) { + return null; + } lock(); try { TimeIndexImpl viewIndex = viewIndexes.get(graph.getView()); @@ -339,8 +342,10 @@ public void indexView(Graph graph) { K[] ts = set.toArray(); int tsLength = ts.length; for (int i = 0; i < tsLength; i++) { - int timestamp = timeSortedMap.get(ts[i]); - viewIndex.add(timestamp, element); + Integer timestamp = timeSortedMap.get(ts[i]); + if (timestamp != null) { + viewIndex.add(timestamp, element); + } } } } @@ -362,8 +367,10 @@ public void indexInView(T element, GraphView view) { K[] ts = set.toArray(); int tsLength = ts.length; for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - viewIndex.add(timestampIndex, element); + Integer timestampIndex = timeSortedMap.get(ts[i]); + if (timestampIndex != null) { + viewIndex.add(timestampIndex, element); + } } } } finally { @@ -382,8 +389,10 @@ public void clearInView(T element, GraphView view) { K[] ts = set.toArray(); int tsLength = ts.length; for (int i = 0; i < tsLength; i++) { - int timestampIndex = timeSortedMap.get(ts[i]); - viewIndex.remove(timestampIndex, element); + Integer timestampIndex = timeSortedMap.get(ts[i]); + if (timestampIndex != null) { + viewIndex.remove(timestampIndex, element); + } } } } finally { diff --git a/src/main/java/org/gephi/graph/impl/TimeStore.java b/src/main/java/org/gephi/graph/impl/TimeStore.java index 5556e568..292894bc 100644 --- a/src/main/java/org/gephi/graph/impl/TimeStore.java +++ b/src/main/java/org/gephi/graph/impl/TimeStore.java @@ -47,8 +47,7 @@ public TimeStore(GraphStore store, boolean indexed) { } public double getMin(Graph graph) { - if (nodeIndexStore == null || edgeIndexStore == null) { - // TODO: Manual calculation + if (!nodeIndexStore.hasIndex() || !edgeIndexStore.hasIndex()) { return Double.NEGATIVE_INFINITY; } double nodeMin = nodeIndexStore.getIndex(graph).getMinTimestamp(); @@ -63,8 +62,7 @@ public double getMin(Graph graph) { } public double getMax(Graph graph) { - if (nodeIndexStore == null || edgeIndexStore == null) { - // TODO: Manual calculation + if (!nodeIndexStore.hasIndex() || !edgeIndexStore.hasIndex()) { return Double.POSITIVE_INFINITY; } double nodeMax = nodeIndexStore.getIndex(graph).getMaxTimestamp(); diff --git a/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java index 9730f494..9c25d165 100644 --- a/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java @@ -619,6 +619,33 @@ public void testClearViewWithView() { Assert.assertFalse(index.hasElements()); } + @Test + public void testGetIndexViewNotIndexed() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(TimeRepresentation.INTERVAL); + IntervalIndexStore store = new IntervalIndexStore<>(Node.class, null, false); + GraphView view = graphStore.viewStore.createView(); + Graph graph = graphStore.viewStore.getGraph(view); + Assert.assertNull(store.getIndex(graph)); + } + + @Test + public void testClearInViewElementNotInView() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(TimeRepresentation.INTERVAL); + NodeImpl n1 = graphStore.getNode("1"); + n1.addInterval(new Interval(1.0, 2.0)); + + IntervalIndexStore store = (IntervalIndexStore) graphStore.timeStore.nodeIndexStore; + + GraphViewImpl view = graphStore.viewStore.createView(); + Graph graph = graphStore.viewStore.getGraph(view); + store.createViewIndex(graph); + + // n1 has interval [1,2] in timeSortedMap but was never added to the (empty) + // view index + // clearInView previously threw ArrayIndexOutOfBoundsException + store.clearInView(n1, view); + } + // UTILITY private Object[] getArrayFromIterable(Iterable iterable) { List list = new ArrayList<>(); diff --git a/src/test/java/org/gephi/graph/impl/TimeStoreTest.java b/src/test/java/org/gephi/graph/impl/TimeStoreTest.java index ee7ad883..cf495651 100644 --- a/src/test/java/org/gephi/graph/impl/TimeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/TimeStoreTest.java @@ -53,6 +53,20 @@ public void testGetMaxNull() { Assert.assertEquals(store.getMax(graphStore), Double.POSITIVE_INFINITY); } + @Test + public void testGetMinNotIndexed() { + GraphStore graphStore = new GraphStore(); + TimeStore store = new TimeStore(graphStore, false); + Assert.assertEquals(store.getMin(graphStore), Double.NEGATIVE_INFINITY); + } + + @Test + public void testGetMaxNotIndexed() { + GraphStore graphStore = new GraphStore(); + TimeStore store = new TimeStore(graphStore, false); + Assert.assertEquals(store.getMax(graphStore), Double.POSITIVE_INFINITY); + } + @Test public void testGetMin() { GraphStore graphStore = new GraphStore(); diff --git a/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java b/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java index a6f2e1c5..7b06fc75 100644 --- a/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java @@ -643,6 +643,33 @@ public void testClearViewWithView() { Assert.assertFalse(index.hasElements()); } + @Test + public void testGetIndexViewNotIndexed() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + TimestampIndexStore store = new TimestampIndexStore<>(Node.class, null, false); + GraphView view = graphStore.viewStore.createView(); + Graph graph = graphStore.viewStore.getGraph(view); + Assert.assertNull(store.getIndex(graph)); + } + + @Test + public void testClearInViewElementNotInView() { + GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); + NodeImpl n1 = graphStore.getNode("1"); + n1.addTimestamp(1.0); + + TimestampIndexStore store = (TimestampIndexStore) graphStore.timeStore.nodeIndexStore; + + GraphViewImpl view = graphStore.viewStore.createView(); + Graph graph = graphStore.viewStore.getGraph(view); + store.createViewIndex(graph); + + // n1 has timestamp 1.0 in timeSortedMap but was never added to the (empty) view + // index + // clearInView previously threw ArrayIndexOutOfBoundsException + store.clearInView(n1, view); + } + // UTILITY private Object[] getArrayFromIterable(Iterable iterable) { List list = new ArrayList<>(); From e2aae73f0f5d941ed47589dc8284ed0a0840f1f6 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 23 Apr 2026 20:36:41 +0200 Subject: [PATCH 235/271] Fix minor serialization issue with idmap not being purged --- .../org/gephi/graph/impl/Serialization.java | 2 + .../gephi/graph/impl/SerializationTest.java | 44 ++++++++++++++----- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 8e535440..2ae91f03 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -348,6 +348,8 @@ public GraphStore deserializeGraphStore(DataInput is) throws IOException, ClassN throw new IOException("The store is not empty"); } + idMap.clear(); + // Store Configuration deserialize(is); diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index a14ac49c..028e8050 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -109,11 +109,12 @@ public void testEdgeStoreMixed() throws IOException, ClassNotFoundException { Serialization ser = new Serialization(graphModel); byte[] buf = ser.serialize(graphStore); - graphStore.clear(); + GraphModelImpl graphModel2 = new GraphModelImpl(); + ser = new Serialization(graphModel2); GraphStore l = (GraphStore) ser.deserialize(buf); - Assert.assertTrue(nodeStore.equals(l.nodeStore)); - Assert.assertTrue(edgeStore.equals(l.edgeStore)); + Assert.assertTrue(nodeStore.deepEquals(l.nodeStore)); + Assert.assertTrue(edgeStore.deepEquals(l.edgeStore)); } @Test @@ -130,11 +131,12 @@ public void testEdgeStoreMultipleTypes() throws IOException, ClassNotFoundExcept Serialization ser = new Serialization(graphModel); byte[] buf = ser.serialize(graphStore); - graphStore.clear(); + GraphModelImpl graphModel2 = new GraphModelImpl(); + ser = new Serialization(graphModel2); GraphStore l = (GraphStore) ser.deserialize(buf); - Assert.assertTrue(nodeStore.equals(l.nodeStore)); - Assert.assertTrue(edgeStore.equals(l.edgeStore)); + Assert.assertTrue(nodeStore.deepEquals(l.nodeStore)); + Assert.assertTrue(edgeStore.deepEquals(l.edgeStore)); } @Test @@ -146,16 +148,17 @@ public void testEdgeStore() throws IOException, ClassNotFoundException { EdgeStore edgeStore = graphStore.edgeStore; NodeImpl[] nodes = GraphGenerator.generateSmallNodeList(); nodeStore.addAll(Arrays.asList(nodes)); - EdgeImpl[] edges = GraphGenerator.generateSmallEdgeList(); + EdgeImpl[] edges = GraphGenerator.generateEdgeList(nodeStore, 100, 0, true, true, false); edgeStore.addAll(Arrays.asList(edges)); Serialization ser = new Serialization(graphModel); byte[] buf = ser.serialize(graphStore); - graphStore.clear(); + GraphModelImpl graphModel2 = new GraphModelImpl(); + ser = new Serialization(graphModel2); GraphStore l = (GraphStore) ser.deserialize(buf); - Assert.assertTrue(nodeStore.equals(l.nodeStore)); - Assert.assertTrue(edgeStore.equals(l.edgeStore)); + Assert.assertTrue(nodeStore.deepEquals(l.nodeStore)); + Assert.assertTrue(edgeStore.deepEquals(l.edgeStore)); } @Test @@ -1268,6 +1271,27 @@ public void serializeGraphModel(DataOutput out, GraphModelImpl model) throws IOE Assert.assertTrue(read.deepEquals(gm)); } + @Test + public void testReuseSerializationInstanceForDeserialization() throws Exception { + GraphModelImpl gm1 = GraphGenerator.generateSmallGraphStore().graphModel; + GraphModelImpl gm2 = GraphGenerator.generateSmallMultiTypeGraphStore().graphModel; + + DataInputOutput dio1 = new DataInputOutput(); + new Serialization(gm1).serializeGraphModel(dio1, gm1); + byte[] bytes1 = dio1.toByteArray(); + + DataInputOutput dio2 = new DataInputOutput(); + new Serialization(gm2).serializeGraphModel(dio2, gm2); + byte[] bytes2 = dio2.toByteArray(); + + Serialization reused = new Serialization(); + GraphModelImpl read1 = reused.deserializeGraphModel(dio1.reset(bytes1)); + Assert.assertTrue(read1.deepEquals(gm1)); + + GraphModelImpl read2 = reused.deserializeGraphModel(dio2.reset(bytes2)); + Assert.assertTrue(read2.deepEquals(gm2)); + } + @Test public void testBitVectorEqual() throws Exception { Serialization ser = new Serialization(); From 24513315094902ad00917b61de5bc18a9acfdee5 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 23 Apr 2026 20:49:16 +0200 Subject: [PATCH 236/271] Fix minor issues on view store --- .../org/gephi/graph/impl/GraphViewStore.java | 14 ++- .../gephi/graph/impl/GraphViewStoreTest.java | 115 ++++++++++++++++++ 2 files changed, 123 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/GraphViewStore.java b/src/main/java/org/gephi/graph/impl/GraphViewStore.java index 01b86158..4df43564 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewStore.java @@ -82,6 +82,7 @@ public GraphViewImpl createView(GraphView view) { } public GraphViewImpl createView(GraphView view, boolean nodes, boolean edges) { + checkNonNullViewObject(view); if (view.isMainView()) { graphStore.autoWriteLock(); try { @@ -93,7 +94,6 @@ public GraphViewImpl createView(GraphView view, boolean nodes, boolean edges) { graphStore.autoWriteUnlock(); } } else { - checkNonNullViewObject(view); checkGraphViewObject(view); checkViewExist((GraphViewImpl) view); @@ -109,14 +109,15 @@ public GraphViewImpl createView(GraphView view, boolean nodes, boolean edges) { } public void destroyView(GraphView view) { + checkNonNullViewObject(view); if (view.isMainView()) { throw new IllegalArgumentException("Can't delete the main view"); } + checkGraphViewObject(view); + checkViewExist((GraphViewImpl) view); + graphStore.autoWriteLock(); try { - checkNonNullViewObject(view); - checkGraphViewObject(view); - TimeIndexStore nodeTimeStore = graphStore.timeStore.nodeIndexStore; if (nodeTimeStore != null) { nodeTimeStore.deleteViewIndex(((GraphViewImpl) view).getDirectedGraph()); @@ -160,6 +161,7 @@ public boolean contains(GraphView view) { graphStore.autoReadLock(); try { checkNonNullViewObject(view); + checkGraphViewObject(view); GraphViewImpl viewImpl = (GraphViewImpl) view; int id = viewImpl.storeId; if (id != NULL_VIEW && id < length && views[id] == view) { @@ -222,7 +224,7 @@ public void setVisibleView(GraphView view) { if (view == null || view == graphStore.mainGraphView) { visibleView = graphStore.mainGraphView; } else { - checkNonNullViewObject(view); + checkGraphViewObject(view); checkViewExist((GraphViewImpl) view); visibleView = view; } @@ -323,7 +325,7 @@ private void ensureArraySize(int index) { public int deepHashCode() { int hash = 5; for (GraphViewImpl view : this.views) { - hash = 67 * hash + view.deepHashCode(); + hash = 67 * hash + (view != null ? view.deepHashCode() : 0); } hash = 67 * hash + this.length; return hash; diff --git a/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java index cbc6531a..cb19f934 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewStoreTest.java @@ -425,4 +425,119 @@ public void testAddRemoveEdgeWithGarbage() { Assert.assertTrue(graphStore.addEdge(e)); Assert.assertTrue(graphStore.removeEdge(e)); } + + @Test + public void testDeepHashCodeAfterDestroy() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + + store.createView(); + GraphViewImpl view2 = store.createView(); + store.destroyView(view2); + + store.deepHashCode(); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testDestroyViewNull() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + + store.destroyView(null); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testDestroyViewForeignStore() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + + GraphStore graphStore2 = GraphGenerator.generateSmallGraphStore(); + GraphViewImpl foreignView = graphStore2.viewStore.createView(); + + store.destroyView(foreignView); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testContainsForeignViewImplementation() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + + store.contains(new GraphView() { + @Override + public GraphModel getGraphModel() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isMainView() { + return false; + } + + @Override + public boolean isNodeView() { + throw new UnsupportedOperationException(); + } + + @Override + public Interval getTimeInterval() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEdgeView() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDestroyed() { + throw new UnsupportedOperationException(); + } + }); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testSetVisibleViewForeignViewImplementation() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + + store.setVisibleView(new GraphView() { + @Override + public GraphModel getGraphModel() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isMainView() { + return false; + } + + @Override + public boolean isNodeView() { + throw new UnsupportedOperationException(); + } + + @Override + public Interval getTimeInterval() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isEdgeView() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDestroyed() { + throw new UnsupportedOperationException(); + } + }); + } + + @Test(expectedExceptions = NullPointerException.class) + public void testCreateViewCopyNull() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewStore store = graphStore.viewStore; + + store.createView((GraphView) null); + } } From 7c3ae623783ab430c29188dcc6c5ac23aa54a769 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 23 Apr 2026 20:51:33 +0200 Subject: [PATCH 237/271] Add comments on a subtile timestamp index min/max behavior --- src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java | 8 ++++++++ .../java/org/gephi/graph/impl/TimestampIndexImpl.java | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java b/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java index 75034b32..0ac940c2 100644 --- a/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/IntervalIndexImpl.java @@ -36,6 +36,11 @@ public double getMinTimestamp() { try { Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; if (mainIndex) { + // Returns the minimum across all tracked intervals, including those that + // belong only to dynamic attribute values (IntervalMap columns) and not to + // element existence (IntervalSet). This intentionally gives the earliest time + // at which any graph data exists, which may be earlier than the first time + // any element is present. View indexes filter to element-only intervals. if (!sortedMap.isEmpty()) { return sortedMap.getLow(); } @@ -63,6 +68,9 @@ public double getMaxTimestamp() { lock(); try { if (mainIndex) { + // Returns the maximum across all tracked intervals, including those that + // belong only to dynamic attribute values (IntervalMap columns) and not to + // element existence (IntervalSet). See getMinTimestamp() for details. Interval2IntTreeMap sortedMap = (Interval2IntTreeMap) timestampIndexStore.timeSortedMap; if (!sortedMap.isEmpty()) { return sortedMap.getHigh(); diff --git a/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java b/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java index c53cc70d..4f0e3dd0 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/TimestampIndexImpl.java @@ -38,6 +38,11 @@ public double getMinTimestamp() { try { Double2IntSortedMap sortedMap = (Double2IntSortedMap) timestampIndexStore.timeSortedMap; if (mainIndex) { + // Returns the minimum across all tracked timestamps, including those that + // belong only to dynamic attribute values (TimeMap columns) and not to + // element existence (TimeSet). This intentionally gives the earliest time + // at which any graph data exists, which may be earlier than the first time + // any element is present. View indexes filter to element-only timestamps. if (!sortedMap.isEmpty()) { return sortedMap.firstDoubleKey(); } @@ -69,6 +74,9 @@ public double getMaxTimestamp() { try { Double2IntSortedMap sortedMap = (Double2IntSortedMap) timestampIndexStore.timeSortedMap; if (mainIndex) { + // Returns the maximum across all tracked timestamps, including those that + // belong only to dynamic attribute values (TimeMap columns) and not to + // element existence (TimeSet). See getMinTimestamp() for details. if (!sortedMap.isEmpty()) { return sortedMap.lastDoubleKey(); } From 43b8f2510553ec9c5974821b1008decf3618c231 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 23 Apr 2026 21:06:00 +0200 Subject: [PATCH 238/271] Fix minor issues on parsing and formatting --- .../org/gephi/graph/api/AttributeUtils.java | 23 ++++++++----------- .../graph/impl/FormattingAndParsingUtils.java | 2 +- .../gephi/graph/impl/TimestampsParser.java | 2 +- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index bb187dbb..65ca7d2f 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -58,6 +58,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -250,9 +251,9 @@ public class AttributeUtils { .parseDefaulting(ChronoField.NANO_OF_SECOND, 0).appendOffset("+HH:MM", "Z").toFormatter() .withZone(GraphStoreConfiguration.DEFAULT_TIME_ZONE); - DATE_PRINTERS_BY_TIMEZONE = new HashMap<>(); - DATE_TIME_PRINTERS_BY_TIMEZONE = new HashMap<>(); - DATE_TIME_PARSERS_BY_TIMEZONE = new HashMap<>(); + DATE_PRINTERS_BY_TIMEZONE = new ConcurrentHashMap<>(); + DATE_TIME_PRINTERS_BY_TIMEZONE = new ConcurrentHashMap<>(); + DATE_TIME_PARSERS_BY_TIMEZONE = new ConcurrentHashMap<>(); DATE_PRINTERS_BY_TIMEZONE.put(DATE_PRINTER.getZone(), DATE_PRINTER); DATE_TIME_PRINTERS_BY_TIMEZONE.put(DATE_TIME_PRINTER.getZone(), DATE_TIME_PRINTER); @@ -305,13 +306,7 @@ private static DateTimeFormatter getDateTimeFormatterByTimeZone(Map baseFormatter.withZone(z)); } private static DateTimeFormatter getDateTimeParserByTimeZone(ZoneId zoneId) { @@ -426,7 +421,7 @@ public static Object parse(String str, Class typeClass, ZoneId zoneId) { // Instant if (typeClass.equals(Instant.class)) { double milliseconds = FormattingAndParsingUtils.parseDateTimeOrTimestamp(str, zoneId); - return Instant.ofEpochMilli((long) milliseconds); + return Instant.ofEpochMilli(Math.round(milliseconds)); } // Interval types: @@ -785,7 +780,7 @@ private static List getStandardizedList(List list) { } if (oCls != null && !(isSimpleType(oCls) || isArrayType(oCls))) { throw new IllegalArgumentException( - "The list contains unsupported type " + oCls.getClass().getCanonicalName()); + "The list contains unsupported type " + oCls.getCanonicalName()); } if (oCls != null) { if (oCls.equals(Integer.class)) { @@ -831,7 +826,7 @@ private static Set getStandardizedSet(Set set) { } if (oCls != null && !(isSimpleType(oCls) || isArrayType(oCls))) { throw new IllegalArgumentException( - "The set contains unsupported type " + oCls.getClass().getCanonicalName()); + "The set contains unsupported type " + oCls.getCanonicalName()); } if (oCls != null) { if (oCls.equals(Integer.class)) { @@ -883,7 +878,7 @@ private static Map getStandardizedMap(Map map) { } if (oCls != null && !isSimpleType(oCls)) { throw new IllegalArgumentException( - "The map contains unsupported key type " + oCls.getClass().getCanonicalName()); + "The map contains unsupported key type " + oCls.getCanonicalName()); } if (oCls != null) { if (oCls.equals(Integer.class)) { diff --git a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java index 0b03be8f..36334224 100644 --- a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java +++ b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java @@ -61,7 +61,7 @@ public static double parseDateTimeOrTimestamp(String timeStr, ZoneId zoneId) thr if (Double.isNaN(value)) { throw new IllegalArgumentException("NaN is not allowed as an interval bound"); } - } catch (Exception ex) { + } catch (NumberFormatException ex) { value = AttributeUtils.parseDateTime(timeStr, zoneId); } diff --git a/src/main/java/org/gephi/graph/impl/TimestampsParser.java b/src/main/java/org/gephi/graph/impl/TimestampsParser.java index 8bd7b090..5e4dcac1 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampsParser.java +++ b/src/main/java/org/gephi/graph/impl/TimestampsParser.java @@ -226,7 +226,7 @@ public static TimestampMap parseTimestampMap(Class typeClass, String i } else if (typeClass.equals(Character.class)) { result = new TimestampCharMap(); } else { - throw new IllegalArgumentException("Unsupported type " + typeClass.getClass().getCanonicalName()); + throw new IllegalArgumentException("Unsupported type " + typeClass.getCanonicalName()); } if (input.equalsIgnoreCase(EMPTY_VALUE)) { From 465b62eb10441aa28199449ff4a7df6de83a39a0 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 23 Apr 2026 21:22:39 +0200 Subject: [PATCH 239/271] Fix minor array parsing issue with null --- .../java/org/gephi/graph/impl/FormattingAndParsingUtils.java | 4 +++- src/test/java/org/gephi/graph/impl/ArraysParserTest.java | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java index 36334224..35de7d3f 100644 --- a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java +++ b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java @@ -168,7 +168,9 @@ protected static String parseValue(StringReader reader) throws IOException { */ protected static T convertValue(Class typeClass, String valString) { Object value; - if (typeClass.equals(Byte.class) || typeClass.equals(byte.class) || typeClass.equals(Short.class) || typeClass + if (typeClass.equals(String.class)) { + value = valString; + } else if (typeClass.equals(Byte.class) || typeClass.equals(byte.class) || typeClass.equals(Short.class) || typeClass .equals(short.class) || typeClass.equals(Integer.class) || typeClass.equals(int.class) || typeClass .equals(Long.class) || typeClass.equals(long.class) || typeClass.equals(BigInteger.class)) { value = parseNumberWithoutDecimals((Class) typeClass, valString); diff --git a/src/test/java/org/gephi/graph/impl/ArraysParserTest.java b/src/test/java/org/gephi/graph/impl/ArraysParserTest.java index 38fe8167..af57c936 100644 --- a/src/test/java/org/gephi/graph/impl/ArraysParserTest.java +++ b/src/test/java/org/gephi/graph/impl/ArraysParserTest.java @@ -144,7 +144,7 @@ public void testParseNull() { String[] a2 = ArraysParser.parseArray(String[].class, "[\"null\", null, 'null', value]"); Assert.assertEquals(new Boolean[] { false, null, false }, a1); - Assert.assertEquals(new String[] { null, null, null, "value" }, a2); + Assert.assertEquals(new String[] { "null", null, "null", "value" }, a2); } @Test(expectedExceptions = IllegalArgumentException.class) From b69c5d9efa4a729b957d9e10ef1f93dbfb63fcec Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 23 Apr 2026 21:34:13 +0200 Subject: [PATCH 240/271] Fix test --- src/test/java/org/gephi/graph/api/AttributeUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/gephi/graph/api/AttributeUtilsTest.java b/src/test/java/org/gephi/graph/api/AttributeUtilsTest.java index 9812f50c..d1194ce5 100644 --- a/src/test/java/org/gephi/graph/api/AttributeUtilsTest.java +++ b/src/test/java/org/gephi/graph/api/AttributeUtilsTest.java @@ -159,7 +159,7 @@ public void testParseArrayTypes() { Assert.assertEquals(AttributeUtils.parse("[-1e6, 1, .001, 2e6]", double[].class).getClass(), double[].class); Assert.assertEquals(AttributeUtils - .parse("[' true ', 'null', null]", String[].class), new String[] { " true ", null, null }); + .parse("[' true ', 'null', null]", String[].class), new String[] { " true ", "null", null }); Assert.assertEquals(AttributeUtils.parse("['null']", Integer[].class), new Integer[] { null }); Assert.assertEquals(AttributeUtils.parse("['null']", Double[].class), new Double[] { null }); Assert.assertEquals(AttributeUtils From 8ddf9caf5c69ea5d228aa3988f5af8dc3f0a3de7 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 23 Apr 2026 21:35:27 +0200 Subject: [PATCH 241/271] Formatting --- src/main/java/org/gephi/graph/api/AttributeUtils.java | 9 +++------ .../org/gephi/graph/impl/FormattingAndParsingUtils.java | 7 ++++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index 65ca7d2f..5c41ba5e 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -779,8 +779,7 @@ private static List getStandardizedList(List list) { } } if (oCls != null && !(isSimpleType(oCls) || isArrayType(oCls))) { - throw new IllegalArgumentException( - "The list contains unsupported type " + oCls.getCanonicalName()); + throw new IllegalArgumentException("The list contains unsupported type " + oCls.getCanonicalName()); } if (oCls != null) { if (oCls.equals(Integer.class)) { @@ -825,8 +824,7 @@ private static Set getStandardizedSet(Set set) { } } if (oCls != null && !(isSimpleType(oCls) || isArrayType(oCls))) { - throw new IllegalArgumentException( - "The set contains unsupported type " + oCls.getCanonicalName()); + throw new IllegalArgumentException("The set contains unsupported type " + oCls.getCanonicalName()); } if (oCls != null) { if (oCls.equals(Integer.class)) { @@ -877,8 +875,7 @@ private static Map getStandardizedMap(Map map) { } } if (oCls != null && !isSimpleType(oCls)) { - throw new IllegalArgumentException( - "The map contains unsupported key type " + oCls.getCanonicalName()); + throw new IllegalArgumentException("The map contains unsupported key type " + oCls.getCanonicalName()); } if (oCls != null) { if (oCls.equals(Integer.class)) { diff --git a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java index 35de7d3f..fc6d52fe 100644 --- a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java +++ b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java @@ -170,9 +170,10 @@ protected static T convertValue(Class typeClass, String valString) { Object value; if (typeClass.equals(String.class)) { value = valString; - } else if (typeClass.equals(Byte.class) || typeClass.equals(byte.class) || typeClass.equals(Short.class) || typeClass - .equals(short.class) || typeClass.equals(Integer.class) || typeClass.equals(int.class) || typeClass - .equals(Long.class) || typeClass.equals(long.class) || typeClass.equals(BigInteger.class)) { + } else if (typeClass.equals(Byte.class) || typeClass.equals(byte.class) || typeClass + .equals(Short.class) || typeClass.equals(short.class) || typeClass.equals(Integer.class) || typeClass + .equals(int.class) || typeClass.equals(Long.class) || typeClass + .equals(long.class) || typeClass.equals(BigInteger.class)) { value = parseNumberWithoutDecimals((Class) typeClass, valString); } else if (typeClass.equals(Float.class) || typeClass.equals(float.class) || typeClass .equals(Double.class) || typeClass.equals(double.class) || typeClass.equals(BigDecimal.class)) { From 75a05a5eb62ab46d628e97cd2a7f16dcba5dfb4d Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 24 Apr 2026 19:33:08 +0200 Subject: [PATCH 242/271] Fix column store locking issues --- .../graph/impl/ColumnStandardIndexImpl.java | 27 +++++++----- .../org/gephi/graph/impl/ColumnStore.java | 43 ++++++++++++++----- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java index f5cd397b..16e047a3 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStandardIndexImpl.java @@ -209,21 +209,27 @@ public Number getMaxValue() { @Override public void destroy() { lock(); - map = null; - nullSet.clear(); - elements = 0; - version.incrementAndGet(); - unlock(); + try { + map = null; + nullSet.clear(); + elements = 0; + version.incrementAndGet(); + } finally { + unlock(); + } } @Override public void clear() { lock(); - map.clear(); - nullSet.clear(); - elements = 0; - version.incrementAndGet(); - unlock(); + try { + map.clear(); + nullSet.clear(); + elements = 0; + version.incrementAndGet(); + } finally { + unlock(); + } } @Override @@ -236,6 +242,7 @@ public Iterable get(K value) { lock(); ValueSet valueSet = getValueSet(value); if (valueSet == null) { + unlock(); return ValueSet.EMPTY; } return new LockableIterable<>(valueSet.set); diff --git a/src/main/java/org/gephi/graph/impl/ColumnStore.java b/src/main/java/org/gephi/graph/impl/ColumnStore.java index 178d2115..c37dc96f 100644 --- a/src/main/java/org/gephi/graph/impl/ColumnStore.java +++ b/src/main/java/org/gephi/graph/impl/ColumnStore.java @@ -154,7 +154,11 @@ public void removeColumn(final Column column) { public void removeColumn(final String key) { checkNonNullObject(key); - removeColumn(getColumn(key)); + ColumnImpl col = getColumn(key); + if (col == null) { + throw new IllegalArgumentException("The column doesnt exist"); + } + removeColumn(col); } public int getColumnIndex(final String key) { @@ -345,6 +349,9 @@ public ColumnStoreIterator() { @Override public boolean hasNext() { + if (pointer != null) { + return true; + } while (index < length && (pointer = columns[index++]) == null) { } if (pointer == null) { @@ -377,20 +384,34 @@ public boolean deepEquals(ColumnStore obj) { } Iterator itr1 = this.iterator(); Iterator itr2 = obj.iterator(); - while (itr1.hasNext()) { - if (!itr2.hasNext()) { - return false; + boolean itr1Closed = false; + boolean itr2Closed = false; + try { + while (itr1.hasNext()) { + if (!itr2.hasNext()) { + itr2Closed = true; + return false; + } + Column c1 = itr1.next(); + Column c2 = itr2.next(); + if (!c1.equals(c2)) { + return false; + } } - Column c1 = itr1.next(); - Column c2 = itr2.next(); - if (!c1.equals(c2)) { + itr1Closed = true; + if (itr2.hasNext()) { return false; } + itr2Closed = true; + return true; + } finally { + if (!itr1Closed) { + this.doBreak(); + } + if (!itr2Closed) { + obj.doBreak(); + } } - if (itr2.hasNext()) { - return false; - } - return true; } public int deepHashCode() { From 473ef4ffc20084a39b9f6d966f70094d73a2d236 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 24 Apr 2026 19:56:29 +0200 Subject: [PATCH 243/271] Fix potential deadlocks --- .../org/gephi/graph/impl/TimeIndexStore.java | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java index a43bbef8..c247aac4 100644 --- a/src/main/java/org/gephi/graph/impl/TimeIndexStore.java +++ b/src/main/java/org/gephi/graph/impl/TimeIndexStore.java @@ -102,9 +102,8 @@ public void add(K k, Element element) { if (!viewIndexes.isEmpty()) { for (Entry entry : viewIndexes.entrySet()) { GraphViewImpl graphView = (GraphViewImpl) entry.getKey(); - DirectedSubgraph graph = graphView.getDirectedGraph(); boolean node = element instanceof Node; - if (node ? graph.contains((Node) element) : graph.contains((Edge) element)) { + if (node ? graphView.containsNode((Node) element) : graphView.containsEdge((Edge) element)) { entry.getValue().add(timeIndex, element); } } @@ -201,46 +200,42 @@ public boolean contains(K k) { } public void index(Element element) { - lock(); - try { + synchronized (element) { S timeSet = getTimeSet(element); - - if (timeSet != null) { - add(timeSet, element); - } - - synchronized (element) { + lock(); + try { + if (timeSet != null) { + add(timeSet, element); + } for (Object val : element.getAttributes()) { if (val instanceof TimeMap) { TimeMap dynamicValue = (TimeMap) val; add(dynamicValue); } } + } finally { + unlock(); } - } finally { - unlock(); } } public void clear(Element element) { - lock(); - try { + synchronized (element) { S timeSet = getTimeSet(element); - - if (timeSet != null) { - remove(timeSet, element); - } - - synchronized (element) { + lock(); + try { + if (timeSet != null) { + remove(timeSet, element); + } for (Object val : element.getAttributes()) { if (val instanceof TimeMap) { TimeMap dynamicValue = (TimeMap) val; remove((M) dynamicValue); } } + } finally { + unlock(); } - } finally { - unlock(); } } @@ -454,7 +449,7 @@ public boolean deepEquals(TimeIndexStore obj) { if (!obj.getClass().equals(getClass())) { return false; } - TimeIndexStore other = (TimeIndexStore) obj; + TimeIndexStore other = obj; if (!other.elementType.equals(elementType)) { return false; } From 9bf95f79129a0d044922834fa305f9104769e3b9 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 24 Apr 2026 20:42:40 +0200 Subject: [PATCH 244/271] Fix nodes/edges are re-added to removedNodes / removedEdges on every subsequent diff because the cache entry is never nulled after detection --- .../gephi/graph/impl/GraphObserverImpl.java | 10 ++-- .../gephi/graph/impl/GraphObserverTest.java | 51 +++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java b/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java index 6a3bef35..f812d122 100644 --- a/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphObserverImpl.java @@ -119,10 +119,11 @@ protected void refreshDiff() { if (nodeVersion < graphVersion.nodeVersion) { int maxStoreId = graphStore.nodeStore.maxStoreId(); - for (Node n : nodeCache) { - NodeImpl nImpl = (NodeImpl) n; + for (int i = 0; i < nodeCache.length; i++) { + NodeImpl nImpl = nodeCache[i]; if (nImpl != null && !graph.contains(nImpl)) { graphDiff.removedNodes.add(nImpl); + nodeCache[i] = null; } } if (maxStoreId > nodeCache.length || maxStoreId < nodeCache.length) { @@ -145,10 +146,11 @@ protected void refreshDiff() { if (edgeVersion < graphVersion.edgeVersion) { int maxStoreId = graphStore.edgeStore.maxStoreId(); - for (Edge e : edgeCache) { - EdgeImpl eImpl = (EdgeImpl) e; + for (int i = 0; i < edgeCache.length; i++) { + EdgeImpl eImpl = edgeCache[i]; if (eImpl != null && !graph.contains(eImpl)) { graphDiff.removedEdges.add(eImpl); + edgeCache[i] = null; } } if (maxStoreId > edgeCache.length || maxStoreId < edgeCache.length) { diff --git a/src/test/java/org/gephi/graph/impl/GraphObserverTest.java b/src/test/java/org/gephi/graph/impl/GraphObserverTest.java index 1e1b15a1..b2066b1e 100644 --- a/src/test/java/org/gephi/graph/impl/GraphObserverTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphObserverTest.java @@ -389,6 +389,57 @@ public void testDiffRemoveAllNodes() { Assert.assertSame(diff.getAddedNodes(), NodeIterable.EMPTY); } + @Test + public void testDiffRemovedNodeNotReportedTwice() { + GraphStore store = GraphGenerator.generateSmallGraphStore(); + GraphObserverImpl graphObserver = store.createGraphObserver(store, true); + graphObserver.hasGraphChanged(); + + Node removed = store.getNodes().toArray()[0]; + store.removeNode(removed); + + // First diff: removal is reported + graphObserver.hasGraphChanged(); + GraphDiff diff1 = graphObserver.getDiff(); + Assert.assertEquals(diff1.getRemovedNodes().toArray().length, 1); + + // Make an unrelated change so a second diff is triggered + store.addNode(store.factory.newNode("extra")); + + // Second diff: the already-reported removal must NOT appear again + graphObserver.hasGraphChanged(); + GraphDiff diff2 = graphObserver.getDiff(); + for (Node n : diff2.getRemovedNodes()) { + Assert.assertNotSame(n, removed); + } + } + + @Test + public void testDiffRemovedEdgeNotReportedTwice() { + GraphStore store = GraphGenerator.generateSmallGraphStore(); + GraphObserverImpl graphObserver = store.createGraphObserver(store, true); + graphObserver.hasGraphChanged(); + + Edge removed = store.getEdges().toArray()[0]; + store.removeEdge(removed); + + // First diff: removal is reported + graphObserver.hasGraphChanged(); + GraphDiff diff1 = graphObserver.getDiff(); + Assert.assertEquals(diff1.getRemovedEdges().toArray().length, 1); + + // Make an unrelated change so a second diff is triggered + Node[] ns = store.getNodes().toArray(); + store.addEdge(store.factory.newEdge("extra", ns[0], ns[1], 0, 1.0, true)); + + // Second diff: the already-reported removal must NOT appear again + graphObserver.hasGraphChanged(); + GraphDiff diff2 = graphObserver.getDiff(); + for (Edge e : diff2.getRemovedEdges()) { + Assert.assertNotSame(e, removed); + } + } + @Test public void testDiffReplaceNode() { GraphStore store = GraphGenerator.generateSmallGraphStore(); From 62a24bf0e02b51d30334dfac32778113c9192729 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 24 Apr 2026 20:48:47 +0200 Subject: [PATCH 245/271] Fix issue #272 GraphViewImpl.fill() and not() report wrong per-type edge counts when parallel edges exist --- .../org/gephi/graph/impl/GraphViewImpl.java | 9 +++---- .../gephi/graph/impl/GraphViewImplTest.java | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index 0eece38f..168c9a5e 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -468,12 +468,9 @@ public void fill() { edgeBitVector.set(0, graphStore.edgeStore.maxStoreId()); this.edgeCount = graphStore.edgeStore.size(); - int typeLength = graphStore.edgeStore.longDictionary.length; + int typeLength = graphStore.edgeStore.typeSize.length; this.typeCounts = new int[typeLength]; - for (int i = 0; i < typeLength; i++) { - int count = graphStore.edgeStore.longDictionary[i].size(); - this.typeCounts[i] = count; - } + System.arraycopy(graphStore.edgeStore.typeSize, 0, this.typeCounts, 0, typeLength); this.mutualEdgeTypeCounts = new int[graphStore.edgeStore.mutualEdgesTypeSize.length]; System.arraycopy(graphStore.edgeStore.mutualEdgesTypeSize, 0, this.mutualEdgeTypeCounts, 0, this.mutualEdgeTypeCounts.length); this.mutualEdgesCount = graphStore.edgeStore.mutualEdgesSize; @@ -598,7 +595,7 @@ public void not() { // Invert all type counts (including types that weren't in the view before) for (int i = 0; i < storeTypeLength; i++) { - this.typeCounts[i] = graphStore.edgeStore.longDictionary[i].size() - this.typeCounts[i]; + this.typeCounts[i] = graphStore.edgeStore.typeSize[i] - this.typeCounts[i]; } for (int i = 0; i < graphStore.edgeStore.mutualEdgesTypeSize.length; i++) { this.mutualEdgeTypeCounts[i] = graphStore.edgeStore.mutualEdgesTypeSize[i] - this.mutualEdgeTypeCounts[i]; diff --git a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java index 5092c72c..91cf7805 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java @@ -65,6 +65,33 @@ public void testFill() { } } + @Test + public void testFillTypeCountsWithParallelEdges() { + // Regression test: fill() used longDictionary[i].size() (distinct source-target + // pairs) instead of typeSize[i] (actual edge count), producing wrong typeCounts + // when parallel edges of the same type exist. + GraphModelImpl model = new GraphModelImpl( + org.gephi.graph.api.Configuration.builder().enableParallelEdgesSameType(true).build()); + GraphStore graphStore = model.store; + + Node n1 = graphStore.factory.newNode("1"); + Node n2 = graphStore.factory.newNode("2"); + graphStore.addNode(n1); + graphStore.addNode(n2); + + // Two parallel edges of type 0 between the same pair + Edge e1 = graphStore.factory.newEdge("e1", n1, n2, 0, 1.0, true); + Edge e2 = graphStore.factory.newEdge("e2", n1, n2, 0, 1.0, true); + graphStore.addEdge(e1); + graphStore.addEdge(e2); + + GraphViewImpl view = graphStore.viewStore.createView(); + view.fill(); + + Assert.assertEquals(view.getEdgeCount(), 2); + Assert.assertEquals(view.getEdgeCount(0), 2); + } + @Test public void testMainView() { GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); From c0b3970540f4ca0444fd9291b5e0a2d7ec5ad40c Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 24 Apr 2026 20:49:19 +0200 Subject: [PATCH 246/271] Fix containsAll(emptyCollection) returns false instead of true, violating the Collection contract --- src/main/java/org/gephi/graph/impl/EdgeStore.java | 2 +- src/main/java/org/gephi/graph/impl/NodeStore.java | 2 +- src/test/java/org/gephi/graph/impl/EdgeStoreTest.java | 8 ++++++++ src/test/java/org/gephi/graph/impl/NodeStoreTest.java | 8 ++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index 5183514d..f109c77b 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -1033,7 +1033,7 @@ public boolean containsAll(Collection c) { } return found == c.size(); } - return false; + return true; } @Override diff --git a/src/main/java/org/gephi/graph/impl/NodeStore.java b/src/main/java/org/gephi/graph/impl/NodeStore.java index 08fa1d68..d4f7dc00 100644 --- a/src/main/java/org/gephi/graph/impl/NodeStore.java +++ b/src/main/java/org/gephi/graph/impl/NodeStore.java @@ -404,7 +404,7 @@ public boolean containsAll(final Collection c) { } return found == c.size(); } - return false; + return true; } @Override diff --git a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java index 205a5c33..58538ea2 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java @@ -369,6 +369,14 @@ public void testContainsAll() { Assert.assertTrue(edgeStore.containsAll(Arrays.asList(edges))); } + @Test + public void testContainsAllEmpty() { + EdgeStore edgeStore = new EdgeStore(); + EdgeImpl[] edges = GraphGenerator.generateEdgeList(3); + edgeStore.addAll(Arrays.asList(edges)); + Assert.assertTrue(edgeStore.containsAll(new java.util.ArrayList<>())); + } + @Test public void testIterator() { EdgeStore edgeStore = new EdgeStore(); diff --git a/src/test/java/org/gephi/graph/impl/NodeStoreTest.java b/src/test/java/org/gephi/graph/impl/NodeStoreTest.java index 09b89d40..d88b9cab 100644 --- a/src/test/java/org/gephi/graph/impl/NodeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/NodeStoreTest.java @@ -333,6 +333,14 @@ public void testContainsAll() { Assert.assertTrue(nodeStore.containsAll(Arrays.asList(nodes))); } + @Test + public void testContainsAllEmpty() { + NodeStore nodeStore = new NodeStore(); + NodeImpl[] nodes = new NodeImpl[] { new NodeImpl("0"), new NodeImpl("1") }; + nodeStore.addAll(Arrays.asList(nodes)); + Assert.assertTrue(nodeStore.containsAll(new java.util.ArrayList<>())); + } + @Test public void testIterator() { NodeStore nodeStore = new NodeStore(); From 97927ec63ba8708708a3e0639d39b6a6cf007f56 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 24 Apr 2026 20:49:46 +0200 Subject: [PATCH 247/271] Fix clear() and clearEdges() bypass the view store callbacks, leaving all live views with stale bit-vectors, counts, and type arrays --- .../java/org/gephi/graph/impl/GraphStore.java | 10 ++++++ .../org/gephi/graph/impl/GraphStoreTest.java | 32 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index d93aff11..65bbbcc6 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -605,6 +605,11 @@ public void clearEdges(final Node node, final int type) { public void clear() { autoWriteLock(); try { + for (GraphViewImpl view : viewStore.views) { + if (view != null) { + view.clear(); + } + } edgeStore.clear(); nodeStore.clear(); edgeTypeStore.clear(); @@ -620,6 +625,11 @@ public void clear() { public void clearEdges() { autoWriteLock(); try { + for (GraphViewImpl view : viewStore.views) { + if (view != null) { + view.clearEdges(); + } + } edgeStore.clear(); edgeTypeStore.clear(); edgeTable.store.indexStore.clear(); diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index d3ee961a..51a1d4a4 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -899,6 +899,38 @@ public void testClearEdges() { Assert.assertEquals(graphStore.getEdgeCount(), 0); } + @Test + public void testClearUpdatesViews() { + // Regression test: clear() did not propagate to views, leaving them with + // stale bit-vectors and counts. + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewImpl view = graphStore.viewStore.createView(); + view.fill(); + + Assert.assertEquals(view.getNodeCount(), graphStore.getNodeCount()); + Assert.assertEquals(view.getEdgeCount(), graphStore.getEdgeCount()); + + graphStore.clear(); + + Assert.assertEquals(view.getNodeCount(), 0); + Assert.assertEquals(view.getEdgeCount(), 0); + } + + @Test + public void testClearEdgesUpdatesViews() { + // Regression test: clearEdges() did not propagate to views. + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + GraphViewImpl view = graphStore.viewStore.createView(); + view.fill(); + + Assert.assertTrue(view.getEdgeCount() > 0); + + graphStore.clearEdges(); + + Assert.assertEquals(view.getEdgeCount(), 0); + Assert.assertEquals(view.getNodeCount(), graphStore.getNodeCount()); + } + @Test public void testClearEdgesByType() { GraphStore graphStore = GraphGenerator.generateTinyGraphStore(); From 2c9643155eda1b2e7faed828217a6b7845cfe1a6 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 24 Apr 2026 20:57:03 +0200 Subject: [PATCH 248/271] Set version to 0.8.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b6c58d89..8413f8e9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.5-SNAPSHOT + 0.8.5 jar GraphStore From 4358272d061b40ae2480ef56822aa6c9d956da5e Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Fri, 24 Apr 2026 21:00:30 +0200 Subject: [PATCH 249/271] Set version to 0.8.6-SNAPSHOT --- README.md | 4 ++-- pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d8e638d1..e613ead2 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.8.4 + 0.8.5 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.8.4' +compile 'org.gephi:graphstore:0.8.5' ``` ## Dependencies diff --git a/pom.xml b/pom.xml index 8413f8e9..ff4e0a74 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.5 + 0.8.6-SNAPSHOT jar GraphStore From cd8daf0252e6e46f3891c12bce1f096c97a77b16 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 9 May 2026 08:24:45 +0200 Subject: [PATCH 250/271] Fix issue #273 Parallel stream collect on filtered edge / node-view spliterators throws IllegalStateException: Accept exceeded fixed size --- .../java/org/gephi/graph/impl/EdgeStore.java | 19 ++++++- .../gephi/graph/impl/GraphViewDecorator.java | 18 ++++--- .../org/gephi/graph/impl/EdgeStoreTest.java | 35 +++++++++++++ .../gephi/graph/impl/GraphViewImplTest.java | 52 +++++++++++++++++++ 4 files changed, 116 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index f109c77b..f9912666 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -2237,10 +2237,15 @@ public int characteristics() { private class FilteredSizedEdgeSpliterator extends EdgeSpliterator { protected final Predicate filter; + // True only for the root spliterator, where totalSize is the caller-supplied, + // filter-aware count. Sub-ranges created by trySplit fall back to the inherited, + // unfiltered block count and must drop SIZED. + protected boolean exactSize; FilteredSizedEdgeSpliterator(int startBlock, int endBlockExclusive, Predicate filter, int totalSize) { super(startBlock, endBlockExclusive, totalSize); this.filter = filter; + this.exactSize = (startBlock == 0 && endBlockExclusive == blocksCount); } @Override @@ -2260,13 +2265,25 @@ public boolean tryAdvance(Consumer action) { return false; } + @Override + public Spliterator trySplit() { + Spliterator left = super.trySplit(); + if (left != null) { + // Once split, neither half can guarantee an exact filtered size, so drop SIZED. + this.exactSize = false; + ((FilteredSizedEdgeSpliterator) left).exactSize = false; + } + return left; + } + protected EdgeSpliterator createSplit(int startBlock, int endBlockExclusive) { return new FilteredSizedEdgeSpliterator(startBlock, endBlockExclusive, filter, totalSize); } @Override public int characteristics() { - return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SIZED; + int base = Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL; + return exactSize ? base | Spliterator.SIZED : base; } } diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 062cd69b..1d4f83c8 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -970,6 +970,9 @@ private final class NodeViewSpliterator implements Spliterator { private final int expectedVersion; private int totalSize; private int consumed; + // True only for the root spliterator, where totalSize comes from view.getNodeCount(). + // Sub-ranges only have a proportional estimate and must drop SIZED. + private boolean exactSize; NodeViewSpliterator() { this(0, graphStore.nodeStore.blocksCount); @@ -981,13 +984,13 @@ private final class NodeViewSpliterator implements Spliterator { this.expectedVersion = graphStore.version != null ? graphStore.version.getNodeVersion() : 0; this.consumed = 0; - // Use the view's node count for exact sizing - // Use the total store size for the root spliterator (covering all blocks) + // Root spliterator uses the exact view count; sub-ranges fall back to an estimate. if (startBlock == 0 && endBlockExclusive == graphStore.nodeStore.blocksCount) { this.totalSize = view.getNodeCount(); + this.exactSize = true; } else { - // For split spliterators, compute proportionally this.totalSize = computeSizeEstimate(startBlock, endBlockExclusive); + this.exactSize = false; } if (startBlock < endBlockExclusive) { @@ -1088,8 +1091,10 @@ public Spliterator trySplit() { indexInBlock = 0; } - // Update this spliterator size this.totalSize = Math.max(0, totalSize - left.totalSize); + // Once split, neither half can guarantee an exact view-aware size, so drop SIZED. + this.exactSize = false; + left.exactSize = false; return left; } @@ -1103,9 +1108,8 @@ public long estimateSize() { @Override public int characteristics() { - // SIZED because we know the exact count from view.getNodeCount() - // But not SUBSIZED because splits can't guarantee exact size distribution - return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SIZED; + int base = Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL; + return exactSize ? base | Spliterator.SIZED : base; } } diff --git a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java index 58538ea2..2c1a313e 100644 --- a/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/EdgeStoreTest.java @@ -1963,6 +1963,41 @@ public void testFilteredSpliteratorCharacteristicsAndEstimate() { Assert.assertEquals(sp.estimateSize(), expected.size() - 5); } + @Test + public void testFilteredSpliteratorParallelCollectAcrossBlocks() { + // Regression: FilteredSizedEdgeSpliterator used to keep SIZED after splitting, with a + // per-half totalSize derived from the unfiltered range count. Parallel collect would + // then fail with "Accept exceeded fixed size of N" inside FixedNodeBuilder. + int undirectedCount = GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE + (GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE / 2); + EdgeImpl[] edges = GraphGenerator.generateEdgeList(undirectedCount, 0, false, true, false); + EdgeStore edgeStore = new EdgeStore(); + edgeStore.addAll(Arrays.asList(edges)); + + int expectedUndirected = edgeStore.undirectedSize(); + Assert.assertTrue(edgeStore.blocksCount > 1, "Need multiple blocks to exercise trySplit"); + + List collected = StreamSupport.stream(edgeStore.spliteratorUndirected(), true) + .collect(Collectors.toList()); + Assert.assertEquals(collected.size(), expectedUndirected); + Assert.assertEquals(new HashSet<>(collected).size(), expectedUndirected); + } + + @Test + public void testFilteredSpliteratorSplitDropsSizedCharacteristic() { + int undirectedCount = GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE + 256; + EdgeImpl[] edges = GraphGenerator.generateEdgeList(undirectedCount, 0, false, true, false); + EdgeStore edgeStore = new EdgeStore(); + edgeStore.addAll(Arrays.asList(edges)); + + Spliterator root = edgeStore.spliteratorUndirected(); + Assert.assertTrue((root.characteristics() & Spliterator.SIZED) != 0); + + Spliterator left = root.trySplit(); + Assert.assertNotNull(left); + Assert.assertTrue((root.characteristics() & Spliterator.SIZED) == 0); + Assert.assertTrue((left.characteristics() & Spliterator.SIZED) == 0); + } + @Test public void testEdgeSpliteratorCoversAll() { NodeStore nodeStore = GraphGenerator.generateNodeStore(2); diff --git a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java index 91cf7805..9606645f 100644 --- a/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphViewImplTest.java @@ -17,8 +17,11 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Spliterator; import org.gephi.graph.api.DirectedSubgraph; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Graph; @@ -548,6 +551,55 @@ public void testNodeView() { } } + @Test + public void testNodeViewSpliteratorParallelCollectAcrossBlocks() { + // Regression: NodeViewSpliterator used to keep SIZED after splitting, with a per-half + // totalSize derived from a proportional estimate. Parallel collect would then fail + // with "Accept exceeded fixed size of N" inside FixedNodeBuilder. The view here only + // contains the first block's nodes, so the proportional estimate undercounts. + GraphStore graphStore = new GraphModelImpl().store; + int totalNodes = GraphStoreConfiguration.NODESTORE_BLOCK_SIZE * 2 + 256; + NodeImpl[] nodes = GraphGenerator.generateNodeList(totalNodes, graphStore); + graphStore.addAllNodes(Arrays.asList(nodes)); + + GraphViewStore viewStore = graphStore.viewStore; + GraphViewImpl view = viewStore.createView(true, false); + int inViewCount = GraphStoreConfiguration.NODESTORE_BLOCK_SIZE; + for (int i = 0; i < inViewCount; i++) { + view.addNode(nodes[i]); + } + Assert.assertEquals(view.getNodeCount(), inViewCount); + Assert.assertTrue(graphStore.nodeStore.blocksCount > 1, "Need multiple blocks to exercise trySplit"); + + Subgraph subgraph = viewStore.getGraph(view); + Collection collected = subgraph.getNodes().toCollection(); + Assert.assertEquals(collected.size(), inViewCount); + Assert.assertEquals(new HashSet<>(collected).size(), inViewCount); + } + + @Test + public void testNodeViewSpliteratorSplitDropsSizedCharacteristic() { + GraphStore graphStore = new GraphModelImpl().store; + int totalNodes = GraphStoreConfiguration.NODESTORE_BLOCK_SIZE + 128; + NodeImpl[] nodes = GraphGenerator.generateNodeList(totalNodes, graphStore); + graphStore.addAllNodes(Arrays.asList(nodes)); + + GraphViewStore viewStore = graphStore.viewStore; + GraphViewImpl view = viewStore.createView(true, false); + for (int i = 0; i < totalNodes / 2; i++) { + view.addNode(nodes[i]); + } + + Subgraph subgraph = viewStore.getGraph(view); + Spliterator root = subgraph.getNodes().spliterator(); + Assert.assertTrue((root.characteristics() & Spliterator.SIZED) != 0); + + Spliterator left = root.trySplit(); + Assert.assertNotNull(left); + Assert.assertTrue((root.characteristics() & Spliterator.SIZED) == 0); + Assert.assertTrue((left.characteristics() & Spliterator.SIZED) == 0); + } + @Test public void testNodeViewEdgeUpdate() { GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); From cfdf091532b661657ee0d7aaa55d499e14a93316 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 9 May 2026 09:38:22 +0200 Subject: [PATCH 251/271] Formatting fix --- formatter-config.xml | 2 +- .../org/gephi/graph/api/AttributeUtils.java | 58 +++----- src/main/java/org/gephi/graph/api/Column.java | 9 +- .../java/org/gephi/graph/api/ColumnDiff.java | 4 +- .../java/org/gephi/graph/api/ColumnIndex.java | 15 +- .../org/gephi/graph/api/ColumnObserver.java | 20 ++- .../org/gephi/graph/api/Configuration.java | 63 ++++---- .../org/gephi/graph/api/DirectedGraph.java | 10 +- src/main/java/org/gephi/graph/api/Edge.java | 6 +- .../org/gephi/graph/api/EdgeIterable.java | 5 +- .../java/org/gephi/graph/api/Element.java | 6 +- .../org/gephi/graph/api/ElementIterable.java | 3 +- .../java/org/gephi/graph/api/Estimator.java | 4 +- src/main/java/org/gephi/graph/api/Graph.java | 13 +- .../java/org/gephi/graph/api/GraphBridge.java | 20 ++- .../java/org/gephi/graph/api/GraphDiff.java | 4 +- .../org/gephi/graph/api/GraphFactory.java | 4 +- .../java/org/gephi/graph/api/GraphLock.java | 51 +++---- .../java/org/gephi/graph/api/GraphModel.java | 135 +++++++----------- .../org/gephi/graph/api/GraphObserver.java | 29 ++-- .../java/org/gephi/graph/api/GraphView.java | 29 ++-- src/main/java/org/gephi/graph/api/Index.java | 17 +-- .../java/org/gephi/graph/api/Interval.java | 27 ++-- .../org/gephi/graph/api/NodeIterable.java | 5 +- src/main/java/org/gephi/graph/api/Rect2D.java | 27 ++-- .../org/gephi/graph/api/SpatialIndex.java | 51 +++---- .../java/org/gephi/graph/api/Subgraph.java | 18 +-- src/main/java/org/gephi/graph/api/Table.java | 3 +- .../java/org/gephi/graph/api/TableDiff.java | 4 +- .../java/org/gephi/graph/api/TableLock.java | 23 ++- .../org/gephi/graph/api/TableObserver.java | 18 ++- .../gephi/graph/api/TimeRepresentation.java | 19 ++- .../graph/api/types/IntervalBooleanMap.java | 11 +- .../graph/api/types/IntervalByteMap.java | 11 +- .../graph/api/types/IntervalCharMap.java | 11 +- .../graph/api/types/IntervalDoubleMap.java | 11 +- .../graph/api/types/IntervalFloatMap.java | 11 +- .../graph/api/types/IntervalIntegerMap.java | 11 +- .../graph/api/types/IntervalLongMap.java | 11 +- .../gephi/graph/api/types/IntervalMap.java | 22 ++- .../gephi/graph/api/types/IntervalSet.java | 15 +- .../graph/api/types/IntervalShortMap.java | 11 +- .../graph/api/types/IntervalStringMap.java | 7 +- .../org/gephi/graph/api/types/TimeMap.java | 7 +- .../org/gephi/graph/api/types/TimeSet.java | 11 +- .../graph/api/types/TimestampBooleanMap.java | 8 +- .../graph/api/types/TimestampByteMap.java | 8 +- .../graph/api/types/TimestampCharMap.java | 8 +- .../graph/api/types/TimestampDoubleMap.java | 8 +- .../graph/api/types/TimestampFloatMap.java | 8 +- .../graph/api/types/TimestampIntegerMap.java | 8 +- .../graph/api/types/TimestampLongMap.java | 8 +- .../gephi/graph/api/types/TimestampMap.java | 15 +- .../gephi/graph/api/types/TimestampSet.java | 4 +- .../graph/api/types/TimestampShortMap.java | 8 +- .../graph/api/types/TimestampStringMap.java | 4 +- .../org/gephi/graph/impl/ArraysParser.java | 6 +- .../java/org/gephi/graph/impl/EdgeStore.java | 16 +-- .../graph/impl/FormattingAndParsingUtils.java | 31 ++-- .../org/gephi/graph/impl/GraphViewImpl.java | 21 ++- .../gephi/graph/impl/Interval2IntTreeMap.java | 23 ++- .../org/gephi/graph/impl/IntervalsParser.java | 75 ++++------ .../org/gephi/graph/impl/NodesQuadTree.java | 5 +- .../gephi/graph/impl/TimestampsParser.java | 63 ++++---- .../gephi/graph/impl/utils/LongPacker.java | 18 ++- .../gephi/graph/impl/utils/MapDeepEquals.java | 5 +- .../java/org/gephi/graph/spi/LayoutData.java | 6 +- 67 files changed, 499 insertions(+), 709 deletions(-) diff --git a/formatter-config.xml b/formatter-config.xml index f2e5930d..bedc4709 100644 --- a/formatter-config.xml +++ b/formatter-config.xml @@ -64,7 +64,7 @@ - + diff --git a/src/main/java/org/gephi/graph/api/AttributeUtils.java b/src/main/java/org/gephi/graph/api/AttributeUtils.java index 5c41ba5e..153eee6d 100644 --- a/src/main/java/org/gephi/graph/api/AttributeUtils.java +++ b/src/main/java/org/gephi/graph/api/AttributeUtils.java @@ -96,10 +96,9 @@ /** * Set of utility methods to manipulate supported attribute types. *

- * The attribute system is built with a set of supported column types. This - * class contains utilities to parse and convert supported types. It also - * contains utilities to manipulate primitive arrays (the preferred array type) - * and date/time types. Default time zone for parsing/printing dates is UTC. + * The attribute system is built with a set of supported column types. This class contains utilities to parse and + * convert supported types. It also contains utilities to manipulate primitive arrays (the preferred array type) and + * date/time types. Default time zone for parsing/printing dates is UTC. */ public class AttributeUtils { @@ -359,15 +358,13 @@ public static String print(Object value, TimeFormat timeFormat, ZoneId zoneId) { } /** - * Parses the given string using the type class provided and returns an - * instance. + * Parses the given string using the type class provided and returns an instance. * * @param str string to parse * @param typeClass class of the desired type - * @param zoneId time zone to use or null to use default time zone (UTC), for - * dynamic types and Instant only - * @return an instance of the type class, or null if str is null or - * empty + * @param zoneId time zone to use or null to use default time zone (UTC), for dynamic types and Instant + * only + * @return an instance of the type class, or null if str is null or empty */ public static Object parse(String str, Class typeClass, ZoneId zoneId) { if (str == null || str.isEmpty()) { @@ -500,15 +497,13 @@ public static Object parse(String str, Class typeClass, ZoneId zoneId) { } /** - * Parses the given string using the type class provided and returns an - * instance. + * Parses the given string using the type class provided and returns an instance. * * Default time zone is used (UTC) for dynamic types (timestamps/intervals). * * @param str string to parse * @param typeClass class of the desired type - * @return an instance of the type class, or null if str is null or - * empty + * @return an instance of the type class, or null if str is null or empty */ public static Object parse(String str, Class typeClass) { return parse(str, typeClass, null); @@ -600,8 +595,7 @@ public static boolean isSupported(Class type) { /** * Returns the standardized type for the given type class. *

- * For instance, getStandardizedType(int.class) would return - * Integer.class. + * For instance, getStandardizedType(int.class) would return Integer.class. * * @param type type to standardize * @return standardized type @@ -733,8 +727,7 @@ public static Class getStaticType(Class type) { } /** - * Transform the given value instance in a standardized type if - * necessary. + * Transform the given value instance in a standardized type if necessary. *

* This function transforms wrapped primitive arrays in primitive arrays. * @@ -1052,8 +1045,7 @@ public static double parseDateTime(String dateTime, ZoneId zoneId) throws DateTi } /** - * Parses the given time and returns its milliseconds representation. Default - * time zone is used (UTC). + * Parses the given time and returns its milliseconds representation. Default time zone is used (UTC). * * @param dateTime the type to parse * @return milliseconds representation @@ -1064,8 +1056,8 @@ public static double parseDateTime(String dateTime) throws DateTimeParseExceptio } /** - * Parses an ISO date with or without time or a timestamp (in milliseconds). - * Returns the date or timestamp converted to a timestamp in milliseconds. + * Parses an ISO date with or without time or a timestamp (in milliseconds). Returns the date or timestamp converted + * to a timestamp in milliseconds. * * @param timeStr Date or timestamp string * @param zoneId Time zone to use or null to use default time zone (UTC) @@ -1077,9 +1069,8 @@ public static double parseDateTimeOrTimestamp(String timeStr, ZoneId zoneId) thr } /** - * Parses an ISO date with or without time or a timestamp (in milliseconds). - * Returns the date or timestamp converted to a timestamp in milliseconds. - * Default time zone is used (UTC). + * Parses an ISO date with or without time or a timestamp (in milliseconds). Returns the date or timestamp converted + * to a timestamp in milliseconds. Default time zone is used (UTC). * * @param timeStr Date or timestamp string * @return Timestamp @@ -1127,8 +1118,7 @@ public static String printDate(Instant instant, ZoneId zoneId) { } /** - * Returns the date's string representation of the given timestamp. Default time - * zone is used (UTC). + * Returns the date's string representation of the given timestamp. Default time zone is used (UTC). * * @param timestamp time, in milliseconds * @return formatted date @@ -1166,8 +1156,7 @@ public static String printDateTime(Instant instant, ZoneId zoneId) { } /** - * Returns the time's string representation of the given timestamp. Default time - * zone is used (UTC). + * Returns the time's string representation of the given timestamp. Default time zone is used (UTC). * * @param timestamp time, in milliseconds * @return formatted time @@ -1198,8 +1187,7 @@ public static String printTimestampInFormat(double timestamp, TimeFormat timeFor } /** - * Returns the string representation of the given timestamp in the given format. - * Default time zone is used (UTC). + * Returns the string representation of the given timestamp in the given format. Default time zone is used (UTC). * * @param timestamp time, in milliseconds * @param timeFormat time format @@ -1210,9 +1198,8 @@ public static String printTimestampInFormat(double timestamp, TimeFormat timeFor } /** - * Returns the string representation of the given array. The used format is the - * same format supported by {@link #parse(java.lang.String, java.lang.Class)} - * method + * Returns the string representation of the given array. The used format is the same format supported by + * {@link #parse(java.lang.String, java.lang.Class)} method * * @param arr Input array. Can be an array of objects or primitives. * @return formatted array @@ -1244,8 +1231,7 @@ public static boolean isEdgeColumn(Column colum) { /** * Returns a copy of the provided object. *

- * The copy is a deep copy for arrays, {@link IntervalSet}, - * {@link TimestampSet}, sets and lists + * The copy is a deep copy for arrays, {@link IntervalSet}, {@link TimestampSet}, sets and lists * * @param obj object to copy * @return copy of the provided object diff --git a/src/main/java/org/gephi/graph/api/Column.java b/src/main/java/org/gephi/graph/api/Column.java index e7928127..9d52d2ed 100644 --- a/src/main/java/org/gephi/graph/api/Column.java +++ b/src/main/java/org/gephi/graph/api/Column.java @@ -18,8 +18,7 @@ /** * A column belongs to a table and represent a dimension in the data. *

- * A column has primarily a unique identifier and a type, which both are set at - * the creation time. + * A column has primarily a unique identifier and a type, which both are set at the creation time. * * @see Table */ @@ -33,8 +32,7 @@ public interface Column { public String getId(); /** - * Returns the column's integer index, which is the position of the column in - * the store. + * Returns the column's integer index, which is the position of the column in the store. * * @return the column's index */ @@ -152,8 +150,7 @@ public interface Column { * * @param withDiff true if column observer should provide column differences * @return the column observer - * @throws UnsupportedOperationException if observers are disabled (from - * Configuration) + * @throws UnsupportedOperationException if observers are disabled (from Configuration) */ public ColumnObserver createColumnObserver(boolean withDiff); } diff --git a/src/main/java/org/gephi/graph/api/ColumnDiff.java b/src/main/java/org/gephi/graph/api/ColumnDiff.java index deaf8703..5252168c 100644 --- a/src/main/java/org/gephi/graph/api/ColumnDiff.java +++ b/src/main/java/org/gephi/graph/api/ColumnDiff.java @@ -18,8 +18,8 @@ /** * Interface to retrieve elements touched in a column. *

- * This interface is associated with a {@link ColumnObserver} and provides an - * easy access to the elements which value has been modified. + * This interface is associated with a {@link ColumnObserver} and provides an easy access to the elements which value + * has been modified. */ public interface ColumnDiff { diff --git a/src/main/java/org/gephi/graph/api/ColumnIndex.java b/src/main/java/org/gephi/graph/api/ColumnIndex.java index 41cf19f7..0508d18f 100644 --- a/src/main/java/org/gephi/graph/api/ColumnIndex.java +++ b/src/main/java/org/gephi/graph/api/ColumnIndex.java @@ -20,9 +20,8 @@ import java.util.Set; /** - * A column index is associated with a column and keeps track of each unique - * value and can also return the minimum and maximum values in case of a - * sortable value type. + * A column index is associated with a column and keeps track of each unique value and can also return the minimum and + * maximum values in case of a sortable value type. * * @param value type * @param Element class @@ -33,8 +32,7 @@ public interface ColumnIndex extends Iterablevalue. * * @param value the value - * @return the number of elements in the column index with value, or - * zero if none + * @return the number of elements in the column index with value, or zero if none */ int count(K value); @@ -68,8 +66,8 @@ public interface ColumnIndex extends Iterable extends Iterable - * Column observer can be used to periodically monitor changes made to a column. - * This scenario is common is multi-threaded applications where a thread is - * responsible to take action when something has changed in the column's data. + * Column observer can be used to periodically monitor changes made to a column. This scenario is common is + * multi-threaded applications where a thread is responsible to take action when something has changed in the column's + * data. *

- * Column observer users should periodically call the - * hasColumnChanged() method to check the status. Each call resets - * the observer so if the method returns true and the table doesn't change after - * that it will return false next time. + * Column observer users should periodically call the hasColumnChanged() method to check the status. Each + * call resets the observer so if the method returns true and the table doesn't change after that it will return false + * next time. *

- * This observer monitors all the rows for this column and consider something - * has changed when an element's value for this column has been changed. + * This observer monitors all the rows for this column and consider something has changed when an element's value for + * this column has been changed. *

- * Observers should be destroyed when not needed anymore. A new observer can be - * obtained from the Column. + * Observers should be destroyed when not needed anymore. A new observer can be obtained from the Column. * * @see Column */ diff --git a/src/main/java/org/gephi/graph/api/Configuration.java b/src/main/java/org/gephi/graph/api/Configuration.java index 3b14ba87..150ee2fd 100644 --- a/src/main/java/org/gephi/graph/api/Configuration.java +++ b/src/main/java/org/gephi/graph/api/Configuration.java @@ -22,9 +22,8 @@ /** * Global configuration set at initialization. *

- * This class can be passed as a parameter to - * {@link GraphModel.Factory#newInstance(org.gephi.graph.api.Configuration)} to - * create a GraphModel with custom configuration. + * This class can be passed as a parameter to {@link GraphModel.Factory#newInstance(org.gephi.graph.api.Configuration)} + * to create a GraphModel with custom configuration. *

* Create instances by using the builder: * @@ -32,11 +31,10 @@ * Configuration config = Configuration.builder().build(); * *

- * Note that setting configurations after the GraphModel has been - * created won't have any effect. + * Note that setting configurations after the GraphModel has been created won't have any effect. *

- * By default, both node and edge id types are String.class and the - * time representation is TIMESTAMP. + * By default, both node and edge id types are String.class and the time representation is + * TIMESTAMP. *

* See the builder documentation for more information on default values. * @@ -171,8 +169,7 @@ public Class getEdgeLabelType() { /** * Sets the edge weight type. *

- * Double, IntervalDoubleMap and - * TimestampDoubleMap are supported. + * Double, IntervalDoubleMap and TimestampDoubleMap are supported. *

* Default is Double.class. * @@ -255,9 +252,8 @@ public boolean isEnableObservers() { /** * Sets whether to enable auto edge type registration. *

- * If enabled, edge types are automatically registered when edges are added. If - * disabled, one needs to call {@link GraphModel#addEdgeType(Object)} explicitly - * for each type. + * If enabled, edge types are automatically registered when edges are added. If disabled, one needs to call + * {@link GraphModel#addEdgeType(Object)} explicitly for each type. *

* Default is true. * @@ -277,8 +273,8 @@ public boolean isEnableAutoEdgeTypeRegistration() { /** * Sets whether to enable node properties. *

- * If enabled, {@link NodeProperties} are created for each node. If those - * properties aren't needed, disabling them can save memory. + * If enabled, {@link NodeProperties} are created for each node. If those properties aren't needed, disabling + * them can save memory. *

* Default is true. * @@ -298,8 +294,8 @@ public boolean isEnableNodeProperties() { /** * Sets whether to enable edge properties. *

- * If enabled, {@link EdgeProperties} are created for each edge. If those - * properties aren't needed, disabling them can save memory. + * If enabled, {@link EdgeProperties} are created for each edge. If those properties aren't needed, disabling + * them can save memory. *

* Default is true. * @@ -319,8 +315,8 @@ public boolean isEnableEdgeProperties() { /** * Sets whether to enable the {@link SpatialIndex}. *

- * If enabled, the spatial index is updated while node positions are updated. If - * unused, disabling it is recommended as it adds some overhead. + * If enabled, the spatial index is updated while node positions are updated. If unused, disabling it is + * recommended as it adds some overhead. *

* The spatial index can be retrieved from {@link Graph#getSpatialIndex()}. *

@@ -342,10 +338,9 @@ public boolean isEnableSpatialIndex() { /** * Sets whether to enable the reverse indexing of node attributes. *

- * If enabled, the reverse index is updated while node attributes are updated. - * This powers {@link GraphModel#getNodeIndex()} but has a negative impact on - * memory usage (as any reverse index does). When disabled, the features of - * {@link Index} are still available but need to iterate over all nodes to + * If enabled, the reverse index is updated while node attributes are updated. This powers + * {@link GraphModel#getNodeIndex()} but has a negative impact on memory usage (as any reverse index does). When + * disabled, the features of {@link Index} are still available but need to iterate over all nodes to * return results. *

* Default is true. @@ -366,10 +361,9 @@ public boolean isEnableIndexNodes() { /** * Sets whether to enable the reverse indexing of edge attributes. *

- * If enabled, the reverse index is updated while node attributes are updated. - * This powers {@link GraphModel#getEdgeIndex()} but has a negative impact on - * memory usage (as any reverse index does). When disabled, the features of - * {@link Index} are still available but need to iterate over all nodes to + * If enabled, the reverse index is updated while node attributes are updated. This powers + * {@link GraphModel#getEdgeIndex()} but has a negative impact on memory usage (as any reverse index does). When + * disabled, the features of {@link Index} are still available but need to iterate over all nodes to * return results. *

* Default is true. @@ -390,10 +384,9 @@ public boolean isEnableIndexEdges() { /** * Sets whether to enable the reverse indexing of timestamps and intervals. *

- * If enabled, the reverse index is updated while element's time set is updated. - * This powers {@link GraphModel#getNodeTimeIndex()} and - * {@link GraphModel#getEdgeTimeIndex()} ()} but has a negative impact on memory - * usage (as any reverse index does). + * If enabled, the reverse index is updated while element's time set is updated. This powers + * {@link GraphModel#getNodeTimeIndex()} and {@link GraphModel#getEdgeTimeIndex()} ()} but has a negative impact + * on memory usage (as any reverse index does). *

* Default is true. * @@ -433,10 +426,9 @@ public boolean isEnableParallelEdgesSameType() { /** * Sets whether to enable auto locking when using read/write APIs. *

- * If disabled, the client is responsible for handling multithreading themselves - * or calling methods such as {@link Graph#readLock()} or - * {@link Graph#writeLock()}. If enabled, each read methods (including - * iterators) handle locking. Similarly, each write method handle locking. + * If disabled, the client is responsible for handling multithreading themselves or calling methods such as + * {@link Graph#readLock()} or {@link Graph#writeLock()}. If enabled, each read methods (including iterators) + * handle locking. Similarly, each write method handle locking. *

* Default is true. * @@ -670,8 +662,7 @@ public String toString() { } /** - * Returns a string representation of the differences between this configuration - * and another one. + * Returns a string representation of the differences between this configuration and another one. * * @param other the other configuration * @return a string representation of the differences diff --git a/src/main/java/org/gephi/graph/api/DirectedGraph.java b/src/main/java/org/gephi/graph/api/DirectedGraph.java index 6ea13a46..08c232b2 100644 --- a/src/main/java/org/gephi/graph/api/DirectedGraph.java +++ b/src/main/java/org/gephi/graph/api/DirectedGraph.java @@ -18,8 +18,8 @@ /** * Directed graph. *

- * This interface has additional methods specific to directed graphs compared to - * the Graph interface it inherits from. + * This interface has additional methods specific to directed graphs compared to the Graph interface it + * inherits from. */ public interface DirectedGraph extends Graph { @@ -55,8 +55,7 @@ public interface DirectedGraph extends Graph { public boolean isAdjacent(Node source, Node target); /** - * Returns true if source and target are adjacent with an edge of the given - * type. + * Returns true if source and target are adjacent with an edge of the given type. * * @param source the source node * @param target the target node @@ -145,8 +144,7 @@ public interface DirectedGraph extends Graph { /** * Gets the edge in the other direction of the given edge. *

- * This takes in account the edge type so only edges of the same type can be - * mutual. + * This takes in account the edge type so only edges of the same type can be mutual. * * @param edge the edge to get the mutual edge * @return the mutual edge, or null diff --git a/src/main/java/org/gephi/graph/api/Edge.java b/src/main/java/org/gephi/graph/api/Edge.java index 79bb8ab0..022678e4 100644 --- a/src/main/java/org/gephi/graph/api/Edge.java +++ b/src/main/java/org/gephi/graph/api/Edge.java @@ -62,8 +62,7 @@ public interface Edge extends Element, EdgeProperties { /** * Returns the edge's weight in the given graph view. *

- * Views can configure a time interval and therefore the edge weight over time - * may vary. + * Views can configure a time interval and therefore the edge weight over time may vary. * * @param view graph view * @return weight @@ -143,8 +142,7 @@ public interface Edge extends Element, EdgeProperties { public boolean isDirected(); /** - * Returns true if this edge is directed and another edge exists in the opposite - * direction. + * Returns true if this edge is directed and another edge exists in the opposite direction. * * @return true if mutual, false otherwise */ diff --git a/src/main/java/org/gephi/graph/api/EdgeIterable.java b/src/main/java/org/gephi/graph/api/EdgeIterable.java index cc9b44dd..79c7b254 100644 --- a/src/main/java/org/gephi/graph/api/EdgeIterable.java +++ b/src/main/java/org/gephi/graph/api/EdgeIterable.java @@ -68,9 +68,8 @@ public interface EdgeIterable extends ElementIterable { /** * Returns a Spliterator over the edges. *

- * Implementations return a splittable, sized, fail-fast spliterator suitable - * for parallel streams. When not possible, a non-splittable spliterator is - * returned. + * Implementations return a splittable, sized, fail-fast spliterator suitable for parallel streams. When not + * possible, a non-splittable spliterator is returned. * * @return edge spliterator */ diff --git a/src/main/java/org/gephi/graph/api/Element.java b/src/main/java/org/gephi/graph/api/Element.java index fdb5002b..f64e84ee 100644 --- a/src/main/java/org/gephi/graph/api/Element.java +++ b/src/main/java/org/gephi/graph/api/Element.java @@ -108,8 +108,7 @@ public interface Element extends ElementProperties { public Object getAttribute(Column column, GraphView view); /** - * Returns an iterable over all the keys and values over time for the given - * (dynamic) column. + * Returns an iterable over all the keys and values over time for the given (dynamic) column. * * @param column column * @return time attribute iterable @@ -322,8 +321,7 @@ public interface Element extends ElementProperties { /** * Gets the time bounds. *

- * The time bounds is an interval made of the minimum and maximum time observed - * in this element. + * The time bounds is an interval made of the minimum and maximum time observed in this element. * * @return time bounds */ diff --git a/src/main/java/org/gephi/graph/api/ElementIterable.java b/src/main/java/org/gephi/graph/api/ElementIterable.java index 2170f492..c22417b6 100644 --- a/src/main/java/org/gephi/graph/api/ElementIterable.java +++ b/src/main/java/org/gephi/graph/api/ElementIterable.java @@ -55,8 +55,7 @@ default Stream stream() { } /** - * Creates a new sequential and parallel stream, based on the spliterator - * returned. + * Creates a new sequential and parallel stream, based on the spliterator returned. * * @return stream */ diff --git a/src/main/java/org/gephi/graph/api/Estimator.java b/src/main/java/org/gephi/graph/api/Estimator.java index a2d502ea..39c23397 100644 --- a/src/main/java/org/gephi/graph/api/Estimator.java +++ b/src/main/java/org/gephi/graph/api/Estimator.java @@ -18,8 +18,8 @@ /** * Estimators specify the strategy to merge attribute values over time. *

- * Estimators are associated with actions that require to transform a sorted set - * of values over time into a single value. + * Estimators are associated with actions that require to transform a sorted set of values over time into a single + * value. */ public enum Estimator { diff --git a/src/main/java/org/gephi/graph/api/Graph.java b/src/main/java/org/gephi/graph/api/Graph.java index 921dba72..217d0d49 100644 --- a/src/main/java/org/gephi/graph/api/Graph.java +++ b/src/main/java/org/gephi/graph/api/Graph.java @@ -90,8 +90,7 @@ public interface Graph { public boolean removeAllNodes(Collection nodes); /** - * Retains only nodes in this graph that are contained in the specified - * collection. + * Retains only nodes in this graph that are contained in the specified collection. * * @param nodes the node collection * @return true if at least one node has been removed, false otherwise @@ -99,8 +98,7 @@ public interface Graph { public boolean retainNodes(Collection nodes); /** - * Retains only edges in this graph that are contained in the specified - * collection. + * Retains only edges in this graph that are contained in the specified collection. * * @param edges the edge collection * @return true if at least one edge has been removed, false otherwise @@ -350,8 +348,7 @@ public interface Graph { * @param node1 the first node * @param node2 the second node * @param type the edge type - * @return true if node1 and node2 are adjacent with an edge og the given type, - * false otherwise + * @return true if node1 and node2 are adjacent with an edge og the given type, false otherwise */ public boolean isAdjacent(Node node1, Node node2, int type); @@ -551,8 +548,8 @@ public interface Graph { public void writeUnlock(); /** - * Returns the graph lock, in case locking is enabled. The graph lock controls - * the multi-thread access to the graph structure. + * Returns the graph lock, in case locking is enabled. The graph lock controls the multi-thread access to the graph + * structure. * * @return graph lock */ diff --git a/src/main/java/org/gephi/graph/api/GraphBridge.java b/src/main/java/org/gephi/graph/api/GraphBridge.java index 3ee26087..8d413104 100644 --- a/src/main/java/org/gephi/graph/api/GraphBridge.java +++ b/src/main/java/org/gephi/graph/api/GraphBridge.java @@ -18,27 +18,23 @@ /** * Helper that helps transfer elements from another graph store. *

- * This bridge can be used to insert elements that belong to another graph in - * this graph store. It operates a deep copy so the destination elements are - * independent from the source and have exactly the same properties and - * attributes. + * This bridge can be used to insert elements that belong to another graph in this graph store. It operates a deep copy + * so the destination elements are independent from the source and have exactly the same properties and attributes. */ public interface GraphBridge { /** * Copy the given nodes to the current graph store. *

- * The nodes typically belong to another graph store. If nodes - * already exists in the current graph they will be ignored. + * The nodes typically belong to another graph store. If nodes already exists in the current graph they + * will be ignored. *

- * All edges attached to nodes will be copied as well if their - * source and target exists in this graph store. + * All edges attached to nodes will be copied as well if their source and target exists in this graph + * store. *

- * This operation takes care of copying attribute columns and values, edge type - * labels and element properties. + * This operation takes care of copying attribute columns and values, edge type labels and element properties. *

- * Beware that the source's configuration should match this graph store - * configuration. + * Beware that the source's configuration should match this graph store configuration. * * @param nodes nodes to copy */ diff --git a/src/main/java/org/gephi/graph/api/GraphDiff.java b/src/main/java/org/gephi/graph/api/GraphDiff.java index a3c2c085..1c95ed51 100644 --- a/src/main/java/org/gephi/graph/api/GraphDiff.java +++ b/src/main/java/org/gephi/graph/api/GraphDiff.java @@ -18,8 +18,8 @@ /** * Interface to retrieve added and removed elements from the graph. *

- * This interface is associated with a {@link GraphObserver} and provides an - * easy access to the elements added or removed. + * This interface is associated with a {@link GraphObserver} and provides an easy access to the elements added or + * removed. */ public interface GraphDiff { diff --git a/src/main/java/org/gephi/graph/api/GraphFactory.java b/src/main/java/org/gephi/graph/api/GraphFactory.java index 36d85cff..fdb7c302 100644 --- a/src/main/java/org/gephi/graph/api/GraphFactory.java +++ b/src/main/java/org/gephi/graph/api/GraphFactory.java @@ -20,8 +20,8 @@ *

* All new nodes and edges are created by this factory. *

- * Both nodes and edges have unique identifiers. If not provided, a unique id - * will be automatically assigned to the elements. + * Both nodes and edges have unique identifiers. If not provided, a unique id will be automatically assigned to the + * elements. */ public interface GraphFactory { diff --git a/src/main/java/org/gephi/graph/api/GraphLock.java b/src/main/java/org/gephi/graph/api/GraphLock.java index 342f1253..e244e2d3 100644 --- a/src/main/java/org/gephi/graph/api/GraphLock.java +++ b/src/main/java/org/gephi/graph/api/GraphLock.java @@ -16,24 +16,21 @@ package org.gephi.graph.api; /** - * Wrapper around ReentrantReadWriteLock that controls multi-thread - * access to the graph structure. + * Wrapper around ReentrantReadWriteLock that controls multi-thread access to the graph structure. */ public interface GraphLock { /** - * Acquires the read lock. Acquires the read lock if the write lock is not held - * by another thread and returns immediately. + * Acquires the read lock. Acquires the read lock if the write lock is not held by another thread and returns + * immediately. */ void readLock(); /** - * Attempts to release this lock. If the number of readers is now zero then the - * lock is made available for write lock attempts. If the current thread does - * not hold this lock then IllegalMonitorStateException is thrown. + * Attempts to release this lock. If the number of readers is now zero then the lock is made available for write + * lock attempts. If the current thread does not hold this lock then IllegalMonitorStateException is thrown. * - * @throws IllegalMonitorStateException if the current thread does not hold this - * lock + * @throws IllegalMonitorStateException if the current thread does not hold this lock */ void readUnlock(); @@ -43,43 +40,37 @@ public interface GraphLock { void readUnlockAll(); /** - * Acquires the write lock. Acquires the write lock if neither the read nor - * write lock are held by another thread and returns immediately, setting the - * write lock hold count to one. + * Acquires the write lock. Acquires the write lock if neither the read nor write lock are held by another thread + * and returns immediately, setting the write lock hold count to one. * - * @throws IllegalMonitorStateException if the current thread holds a read lock - * already + * @throws IllegalMonitorStateException if the current thread holds a read lock already */ void writeLock(); /** - * Attempts to release this lock. If the current thread is the holder of this - * lock then the hold count is decremented. If the hold count is now zero then - * the lock is released. If the current thread is not the holder of this lock - * then IllegalMonitorStateException is thrown. + * Attempts to release this lock. If the current thread is the holder of this lock then the hold count is + * decremented. If the hold count is now zero then the lock is released. If the current thread is not the holder of + * this lock then IllegalMonitorStateException is thrown. *

- * throws @IllegalMonitorStateException if the current thread does not hold this - * lock + * throws @IllegalMonitorStateException if the current thread does not hold this lock */ void writeUnlock(); /** - * Queries the number of reentrant read holds on this lock by the current - * thread. A reader thread has a hold on a lock for each lock action that is not - * matched by an unlock action. + * Queries the number of reentrant read holds on this lock by the current thread. A reader thread has a hold on a + * lock for each lock action that is not matched by an unlock action. * - * @return the number of holds on the read lock by the current thread, or zero - * if the read lock is not held by the current thread + * @return the number of holds on the read lock by the current thread, or zero if the read lock is not held by the + * current thread */ int getReadHoldCount(); /** - * Queries the number of reentrant write holds on this lock by the current - * thread. A writer thread has a hold on a lock for each lock action that is not - * matched by an unlock action. + * Queries the number of reentrant write holds on this lock by the current thread. A writer thread has a hold on a + * lock for each lock action that is not matched by an unlock action. * - * @return the number of holds on the write lock by the current thread, or zero - * if the write lock is not held by the current thread + * @return the number of holds on the write lock by the current thread, or zero if the write lock is not held by the + * current thread */ int getWriteHoldCount(); } diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index 1d4ab30b..d89a1703 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -25,17 +25,14 @@ /** * Graph API's entry point. *

- * GraphModel is the entry point for this API and provide methods - * to create, access and modify graphs. It supports the most common graph - * paradigms and a complete support for graphs over time as well. + * GraphModel is the entry point for this API and provide methods to create, access and modify graphs. It + * supports the most common graph paradigms and a complete support for graphs over time as well. *

    - *
  • Directed: Edges can have a direction. Graphs can be directed, undirected - * or mixed. + *
  • Directed: Edges can have a direction. Graphs can be directed, undirected or mixed. *
  • Weighted: Edges can have a weight. *
  • Self-loops: Nodes can have self-loops. *
  • Labelled edges: Edges can have a label. - *
  • Properties: Each element in the graph can have properties associated to - * it. + *
  • Properties: Each element in the graph can have properties associated to it. *
*

* New instances can be obtained via the embedded factory: @@ -51,44 +48,33 @@ * GraphModel model = GraphModel.Factory.newInstance(configuration); * * - * This API revolves around a set of simple concepts. A GraphModel - * encapsulate all elements and metadata associated with a graph structure. In - * other words it's a single graph, but it also contains configuration, indices, + * This API revolves around a set of simple concepts. A GraphModel encapsulate all elements and metadata + * associated with a graph structure. In other words it's a single graph, but it also contains configuration, indices, * views and other less important services such as observers. *

- * Then, GraphModel gives access to the Graph - * interface, which focuses only on the graph structure and provide methods to - * add, remove, get and iterate nodes and edges. + * Then, GraphModel gives access to the Graph interface, which focuses only on the graph + * structure and provide methods to add, remove, get and iterate nodes and edges. *

- * The Graph contains nodes and edges, which both implement the - * Element interface. This Element interface gives - * access to methods that manipulate the attributes associated to nodes and - * edges. + * The Graph contains nodes and edges, which both implement the Element interface. This + * Element interface gives access to methods that manipulate the attributes associated to nodes and edges. *

- * Any number of attributes can be associated to elements but are managed - * through the Table and Column interfaces. A - * GraphModel gives access by default to a node and edge table. A - * Table is simply a list of columns, which each has a unique - * identifier and a type (e.g. integer). Attribute values can only be associated - * with elements for existing columns. + * Any number of attributes can be associated to elements but are managed through the Table and + * Column interfaces. A GraphModel gives access by default to a node and edge table. A + * Table is simply a list of columns, which each has a unique identifier and a type (e.g. integer). + * Attribute values can only be associated with elements for existing columns. *

- * Attributes are automatically indexed and information such as the number of - * elements with a particular value can be obtained from the Index - * interface. + * Attributes are automatically indexed and information such as the number of elements with a particular value can be + * obtained from the Index interface. *

- * Finally, this API supports the concept of graph views. A view is a mask on - * the graph structure and represents a subgraph. The user controls the set of - * nodes and edges in the view by obtaining a Subgraph for a - * specific GraphView. Views can directly be created and destroyed - * from this model. + * Finally, this API supports the concept of graph views. A view is a mask on the graph structure and represents a + * subgraph. The user controls the set of nodes and edges in the view by obtaining a Subgraph for a + * specific GraphView. Views can directly be created and destroyed from this model. *

* Elements should be created through the {@link #factory() } method. *

- * For performance reasons, edge labels are internally represented as integers - * and the mapping between arbitrary labels is managed through the - * {@link #addEdgeType(java.lang.Object) } and - * {@link #getEdgeType(java.lang.Object) } methods. By default, edges have a - * null label, which is internally represented as zero. + * For performance reasons, edge labels are internally represented as integers and the mapping between arbitrary labels + * is managed through the {@link #addEdgeType(java.lang.Object) } and {@link #getEdgeType(java.lang.Object) } methods. + * By default, edges have a null label, which is internally represented as zero. * * @see Graph * @see Configuration @@ -147,9 +133,8 @@ public static GraphModel read(DataInput input) throws IOException { } /** - * Read the input into the given graph model. The provided graph - * model should be empty and the configurations should match between the - * provided model and the one being read. + * Read the input into the given graph model. The provided graph model should be empty and the + * configurations should match between the provided model and the one being read. * * @param input data input to read from * @return the graphmodel passed as parameter @@ -165,9 +150,8 @@ public static GraphModel read(DataInput input, GraphModel graphModel) throws IOE } /** - * Read the input and return the read graph model without an - * explicit version header in the input. To be used with old graphstore - * serialized data prior to version 0.4 (first, that added the version header). + * Read the input and return the read graph model without an explicit version header in the input. + * To be used with old graphstore serialized data prior to version 0.4 (first, that added the version header). * * @param input data input to read from * @param graphStoreVersion Forced version to use @@ -490,11 +474,10 @@ public static interface DefaultColumns { /** * Creates a new graph view. *

- * By default, the view applies to both nodes and edges, so this is equivalent - * to {@link #createView(boolean, boolean) createView(true, true)}. + * By default, the view applies to both nodes and edges, so this is equivalent to + * {@link #createView(boolean, boolean) createView(true, true)}. *

- * New views are by default empty, i.e. no nodes and no edges are visible in the - * view. + * New views are by default empty, i.e. no nodes and no edges are visible in the view. * * @return newly created graph view */ @@ -503,9 +486,8 @@ public static interface DefaultColumns { /** * Creates a new graph view. *

- * The node and edge filters allows to restrict the view filtering to only nodes - * or only edges. If node only, all edges connected to included nodes will be - * included too. If edge only, all nodes are included but only the edges + * The node and edge filters allows to restrict the view filtering to only nodes or only edges. If node only, all + * edges connected to included nodes will be included too. If edge only, all nodes are included but only the edges * matching the view are included. * * @param nodeFilter predicate to filter nodes, or null to include all nodes @@ -517,9 +499,8 @@ public static interface DefaultColumns { /** * Creates a new graph view. *

- * The node and edge parameters allows to restrict the view filtering to only - * nodes or only edges. If node only, all edges connected to included nodes will - * be included too. If edge only, all nodes are included but only the edges + * The node and edge parameters allows to restrict the view filtering to only nodes or only edges. If node only, all + * edges connected to included nodes will be included too. If edge only, all nodes are included but only the edges * matching the view are included. * * @param node true to enable node view, false otherwise @@ -539,8 +520,8 @@ public static interface DefaultColumns { /** * Creates a new graph based on an existing view. *

- * The node and edge parameters allows to restrict the view filtering to only - * nodes or only edges. By default, the view applies to both nodes and edges. + * The node and edge parameters allows to restrict the view filtering to only nodes or only edges. By default, the + * view applies to both nodes and edges. * * @param view view to copy * @param node true to enable node view, false otherwise @@ -567,22 +548,18 @@ public static interface DefaultColumns { public void setTimeInterval(GraphView view, Interval interval); /** - * Returns the node table. Contains all the columns associated to node - * elements. + * Returns the node table. Contains all the columns associated to node elements. *

- * A GraphModel always has node and edge tables by - * default. + * A GraphModel always has node and edge tables by default. * * @return node table, contains node columns */ public Table getNodeTable(); /** - * Returns the edge table. Contains all the columns associated to edge - * elements. + * Returns the edge table. Contains all the columns associated to edge elements. *

- * A GraphModel always has node and edge tables by - * default. + * A GraphModel always has node and edge tables by default. * * @return edge table, contains edge columns */ @@ -668,8 +645,7 @@ public static interface DefaultColumns { /** * Gets the time bounds. *

- * The time bounds is an interval made of the minimum and maximum time observed - * in the entire graph. + * The time bounds is an interval made of the minimum and maximum time observed in the entire graph. * * @return time bounds */ @@ -678,8 +654,7 @@ public static interface DefaultColumns { /** * Gets the time bounds for the visible graph. *

- * The time bounds is an interval made of the minimum and maximum time observed - * in the entire graph. + * The time bounds is an interval made of the minimum and maximum time observed in the entire graph. * * @return time bounds */ @@ -688,8 +663,7 @@ public static interface DefaultColumns { /** * Gets the time bounds for the given graph view. *

- * The time bounds is an interval made of the minimum and maximum time observed - * in the entire graph. + * The time bounds is an interval made of the minimum and maximum time observed in the entire graph. * * @param view the graph view * @return time bounds @@ -700,8 +674,7 @@ public static interface DefaultColumns { * Creates and returns a new graph observer. * * @param graph the graph to observe - * @param withGraphDiff true to include graph difference feature, false - * otherwise + * @param withGraphDiff true to include graph difference feature, false otherwise * @return newly created graph observer */ public GraphObserver createGraphObserver(Graph graph, boolean withGraphDiff); @@ -744,11 +717,9 @@ public static interface DefaultColumns { /** * Sets a new configuration for this graph model. * - * @deprecated setting configuration after graph model creation is no longer - * supported. Best is to use the {@link Configuration#builder()} to - * create a new configuration and then use it at graph model - * creation from - * {@link GraphModel.Factory#newInstance(Configuration)}. + * @deprecated setting configuration after graph model creation is no longer supported. Best is to use the + * {@link Configuration#builder()} to create a new configuration and then use it at graph model creation + * from {@link GraphModel.Factory#newInstance(Configuration)}. * * @param configuration new configuration */ @@ -758,10 +729,9 @@ public static interface DefaultColumns { /** * Returns the maximum store id number nodes have in this model. *

- * Each node has a unique store identifier which can be retrieved from - * {@link Node#getStoreId() }. This maximum number can help design algorithms - * thar rely on storing nodes in a array. Note that not all consecutive ids may - * be assigned. + * Each node has a unique store identifier which can be retrieved from {@link Node#getStoreId() }. This maximum + * number can help design algorithms thar rely on storing nodes in a array. Note that not all consecutive ids may be + * assigned. * * @return maximum node store id */ @@ -770,10 +740,9 @@ public static interface DefaultColumns { /** * Returns the maximum store id number edges have in this model. *

- * Each edge has a unique store identifier which can be retrieved from - * {@link Edge#getStoreId() }. This maximum number can help design algorithms - * thar rely on storing edges in a array. Note that not all consecutive ids may - * be assigned. + * Each edge has a unique store identifier which can be retrieved from {@link Edge#getStoreId() }. This maximum + * number can help design algorithms thar rely on storing edges in a array. Note that not all consecutive ids may be + * assigned. * * @return maximum edge store id */ diff --git a/src/main/java/org/gephi/graph/api/GraphObserver.java b/src/main/java/org/gephi/graph/api/GraphObserver.java index a5e35330..19d984e5 100644 --- a/src/main/java/org/gephi/graph/api/GraphObserver.java +++ b/src/main/java/org/gephi/graph/api/GraphObserver.java @@ -18,26 +18,22 @@ /** * Observer over a graph to monitor changes and obtain the list of differences. *

- * The graph observer is a mechanism used to monitor periodically changes made - * to the graph. This scenario is common in multi-threaded application where a - * thread is modifying the graph and one or multiple threads need to take action - * when updates are made. + * The graph observer is a mechanism used to monitor periodically changes made to the graph. This scenario is common in + * multi-threaded application where a thread is modifying the graph and one or multiple threads need to take action when + * updates are made. *

- * Graph observer users should periodically call the - * hasGraphChanged() method to check the status. Each call resets - * the observer so if the method returns true and the graph doesn't change after - * that it will return false next time. + * Graph observer users should periodically call the hasGraphChanged() method to check the status. Each + * call resets the observer so if the method returns true and the graph doesn't change after that it will return false + * next time. *

- * In addition of a boolean flag whether the graph has changed, an observer can - * collect data about the differences such as nodes added or removed. Users - * should call the getDiff() method after calling + * In addition of a boolean flag whether the graph has changed, an observer can collect data about the differences such + * as nodes added or removed. Users should call the getDiff() method after calling * hasGraphChanged() to obtain the diff. *

- * Observers should be destroyed when not needed anymore. A new observer can be - * obtained from the GraphModel. + * Observers should be destroyed when not needed anymore. A new observer can be obtained from the + * GraphModel. *

- * Note that observer instances are not thread-safe and should not be called - * from multiple threads simultaneously. + * Note that observer instances are not thread-safe and should not be called from multiple threads simultaneously. * * @see GraphModel */ @@ -77,8 +73,7 @@ public interface GraphObserver { public boolean isDestroyed(); /** - * Returns true if this observer has never got its hasGraphChanged() - * method called. + * Returns true if this observer has never got its hasGraphChanged() method called. * * @return true if new observer, false otherwise */ diff --git a/src/main/java/org/gephi/graph/api/GraphView.java b/src/main/java/org/gephi/graph/api/GraphView.java index 94e233d0..f6f6e035 100644 --- a/src/main/java/org/gephi/graph/api/GraphView.java +++ b/src/main/java/org/gephi/graph/api/GraphView.java @@ -18,27 +18,20 @@ /** * View on the graph. *

- * Each graph can have views and use these views to obtain subgraphs. A view is - * a filter on the main graph structure where some nodes and/or edges are - * missing. + * Each graph can have views and use these views to obtain subgraphs. A view is a filter on the main graph structure + * where some nodes and/or edges are missing. *

- * The graph model has a main view, which is always 100% of nodes and edges. - * Users can then create views and modify them by enabling/disabling elements. - * Views can only have elements which are in the model. As a consequence, if a - * element is removed from the graph it's also removed from all the views. By - * default, the view is empty. + * The graph model has a main view, which is always 100% of nodes and edges. Users can then create views and modify them + * by enabling/disabling elements. Views can only have elements which are in the model. As a consequence, if a element + * is removed from the graph it's also removed from all the views. By default, the view is empty. *

- * The main benefits of views is the ability to obtain a Subgraph - * object from it. Users can call the - * {@link GraphModel#getGraph(org.gephi.graph.api.GraphView) } method and obtain - * a subgraph backed by the view. Update operations such as add or remove on - * this graph are in-fact modifying the view rather than the model. Indeed, - * adding a node to a view is enabling this node in the view. Similarly for - * removal. + * The main benefits of views is the ability to obtain a Subgraph object from it. Users can call the + * {@link GraphModel#getGraph(org.gephi.graph.api.GraphView) } method and obtain a subgraph backed by the view. Update + * operations such as add or remove on this graph are in-fact modifying the view rather than the model. Indeed, adding a + * node to a view is enabling this node in the view. Similarly for removal. *

- * Views can apply on nodes only, edges only or both. This is configured when - * the view is created. Nodes-only view let the system automatically control the - * set of edges. Enabling a node in the view will automatically enable all it's + * Views can apply on nodes only, edges only or both. This is configured when the view is created. Nodes-only view let + * the system automatically control the set of edges. Enabling a node in the view will automatically enable all it's * edges if the opposite nodes are also in the view. * * @see GraphModel diff --git a/src/main/java/org/gephi/graph/api/Index.java b/src/main/java/org/gephi/graph/api/Index.java index 0b31dbbd..12ce559c 100644 --- a/src/main/java/org/gephi/graph/api/Index.java +++ b/src/main/java/org/gephi/graph/api/Index.java @@ -18,8 +18,7 @@ import java.util.Collection; /** - * An index is associated with each table and keeps track of each unique value - * in columns. + * An index is associated with each table and keeps track of each unique value in columns. *

* Each column is associated with a @{{@link ColumnIndex}}. * @@ -32,19 +31,16 @@ public interface Index { * * @param column the column to count values * @param value the value - * @return the number of elements in the index with value in - * column, or zero if none + * @return the number of elements in the index with value in column, or zero if none */ public int count(Column column, Object value); /** - * Gets an Iterable of all elements in the index with value in the - * given column. + * Gets an Iterable of all elements in the index with value in the given column. * * @param column the column to get values * @param value the value - * @return an iterable with element with value in column, or - * null if value not found + * @return an iterable with element with value in column, or null if value not found */ public Iterable get(Column column, Object value); @@ -74,9 +70,8 @@ public interface Index { /** * Returns whether the column is numeric and sortable, and therefore methods - * {@link #getMinValue(org.gephi.graph.api.Column)} and - * {@link #getMaxValue(org.gephi.graph.api.Column)} are available for the - * column. + * {@link #getMinValue(org.gephi.graph.api.Column)} and {@link #getMaxValue(org.gephi.graph.api.Column)} are + * available for the column. * * @param column the column * @return true if the column is sortable, false otherwise diff --git a/src/main/java/org/gephi/graph/api/Interval.java b/src/main/java/org/gephi/graph/api/Interval.java index df6becf5..10b9c257 100644 --- a/src/main/java/org/gephi/graph/api/Interval.java +++ b/src/main/java/org/gephi/graph/api/Interval.java @@ -61,8 +61,8 @@ public Interval(double low, double high) { * Compares this interval with the specified interval for order. * *

- * Any two intervals i and i' satisfy the interval trichotomy; - * that is, exactly one of the following three properties holds: + * Any two intervals i and i' satisfy the interval trichotomy; that is, exactly one of the following + * three properties holds: *

    *
  1. i and i' overlap *
  2. i is to the left of i' @@ -70,16 +70,14 @@ public Interval(double low, double high) { *
* *

- * Note that if two intervals are equal ({@code i.low = i'.low} and - * {@code i.high = i'.high}), they overlap as well. But if they simply overlap - * (for instance {@code i.low < i'.low} and {@code i.high > + * Note that if two intervals are equal ({@code i.low = i'.low} and {@code i.high = i'.high}), they overlap as well. + * But if they simply overlap (for instance {@code i.low < i'.low} and {@code i.high > * i'.high}) they aren't equal. * * @param interval the interval to be compared * - * @return a negative integer, zero, or a positive integer as this interval is - * to the left of, overlaps with, or is to the right of the specified - * interval. + * @return a negative integer, zero, or a positive integer as this interval is to the left of, overlaps with, or is + * to the right of the specified interval. * * @throws NullPointerException if {@code interval} is null. */ @@ -101,9 +99,8 @@ public int compareTo(Interval interval) { * Compares this interval to the given timetamp. * * @param timestamp timestamp - * @return a negative integer, zero or a positive integer if this interval is to - * the left of, overlaps with, or is to the right with the specified - * timestamp. + * @return a negative integer, zero or a positive integer if this interval is to the left of, overlaps with, or is + * to the right with the specified timestamp. * * @throws NullPointerException if {@code timestamp} is null. */ @@ -142,14 +139,12 @@ public double getHigh() { * Compares this interval with the specified object for equality. * *

- * Note that two intervals are equal if {@code i.low = i'.low} and - * {@code i.high = i'.high}. + * Note that two intervals are equal if {@code i.low = i'.low} and {@code i.high = i'.high}. * * @param obj object to which this interval is to be compared * - * @return {@code true} if and only if the specified {@code Object} is a - * {@code Interval} whose low and high are equal to this - * {@code Interval's}. + * @return {@code true} if and only if the specified {@code Object} is a {@code Interval} whose low and high are + * equal to this {@code Interval's}. * */ @Override diff --git a/src/main/java/org/gephi/graph/api/NodeIterable.java b/src/main/java/org/gephi/graph/api/NodeIterable.java index 30d3b2db..e25b0fd2 100644 --- a/src/main/java/org/gephi/graph/api/NodeIterable.java +++ b/src/main/java/org/gephi/graph/api/NodeIterable.java @@ -68,9 +68,8 @@ public interface NodeIterable extends ElementIterable { /** * Returns a Spliterator over the nodes. *

- * Implementations return a splittable, sized, fail-fast spliterator suitable - * for parallel streams. When not possible, a non-splittable spliterator is - * returned. + * Implementations return a splittable, sized, fail-fast spliterator suitable for parallel streams. When not + * possible, a non-splittable spliterator is returned. * * @return node spliterator */ diff --git a/src/main/java/org/gephi/graph/api/Rect2D.java b/src/main/java/org/gephi/graph/api/Rect2D.java index 3970a908..5996a2df 100644 --- a/src/main/java/org/gephi/graph/api/Rect2D.java +++ b/src/main/java/org/gephi/graph/api/Rect2D.java @@ -43,8 +43,7 @@ public Rect2D(Rect2D source) { } /** - * Create a new {@link Rect2D} with the given minimum and maximum corner - * coordinates. + * Create a new {@link Rect2D} with the given minimum and maximum corner coordinates. * * @param minX the x coordinate of the minimum corner * @param minY the y coordinate of the minimum corner @@ -85,8 +84,8 @@ public float height() { } /** - * Return the rectangle's center, as an array where the first element is the x - * coordinate and the second element is the y coordinate. + * Return the rectangle's center, as an array where the first element is the x coordinate and the second element is + * the y coordinate. * * @return the rectangle's center */ @@ -175,14 +174,12 @@ public boolean intersects(float minX, float minY, float maxX, float maxY) { } /** - * Returns true if this rectangle contains or intersects with the given - * rectangle. This is equivalent to checking - * {@code this.contains(rect) || this.intersects(rect)} but more efficient as it - * performs the check in a single operation. + * Returns true if this rectangle contains or intersects with the given rectangle. This is equivalent to checking + * {@code this.contains(rect) || this.intersects(rect)} but more efficient as it performs the check in a single + * operation. * * @param rect the rectangle to check - * @return true if this rectangle contains or intersects with the given - * rectangle, false otherwise + * @return true if this rectangle contains or intersects with the given rectangle, false otherwise */ public boolean containsOrIntersects(Rect2D rect) { if (rect == this) { @@ -193,18 +190,16 @@ public boolean containsOrIntersects(Rect2D rect) { } /** - * Returns true if this rectangle contains or intersects with the given - * rectangle. This is equivalent to checking - * {@code this.contains(minX, minY, maxX, maxY) || this.intersects(minX, minY, maxX, maxY)} - * but more efficient as it performs the check in a single operation. + * Returns true if this rectangle contains or intersects with the given rectangle. This is equivalent to checking + * {@code this.contains(minX, minY, maxX, maxY) || this.intersects(minX, minY, maxX, maxY)} but more efficient as it + * performs the check in a single operation. * * @param minX the x coordinate of the minimum corner * @param minY the y coordinate of the minimum corner * @param maxX the x coordinate of the maximum corner * @param maxY the y coordinate of the maximum corner * - * @return true if this rectangle contains or intersects with the given - * rectangle, false otherwise + * @return true if this rectangle contains or intersects with the given rectangle, false otherwise */ public boolean containsOrIntersects(float minX, float minY, float maxX, float maxY) { // Two rectangles have overlap if they intersect - containment is a subset of diff --git a/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java index a05eccaf..95bc3e6b 100644 --- a/src/main/java/org/gephi/graph/api/SpatialIndex.java +++ b/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -20,16 +20,13 @@ /** * Query the (quadtree-based) index based on the given rectangle area. *

- * The spatial index is not enabled by default. To enable it, set the - * appropriate configuration: + * The spatial index is not enabled by default. To enable it, set the appropriate configuration: * @{@link Configuration.Builder#enableSpatialIndex(boolean)}. *

- * When nodes are moved, added or removed, the spatial index is automatically - * updated. Edges are not indexed, but they are queried based on whether their - * source or target nodes are in the given area. + * When nodes are moved, added or removed, the spatial index is automatically updated. Edges are not indexed, but they + * are queried based on whether their source or target nodes are in the given area. *

- * The Z position is not taken into account when querying the spatial index, - * only X/Y are supported. + * The Z position is not taken into account when querying the spatial index, only X/Y are supported. *

* * @author Eduardo Ramos @@ -56,8 +53,8 @@ public interface SpatialIndex { /** * Returns the nodes in the given area using a faster, but approximate method. *

- * All nodes in the provided area are guaranteed to be returned, but some nodes - * outside the area may also be returned. + * All nodes in the provided area are guaranteed to be returned, but some nodes outside the area may also be + * returned. * * @param rect area to query * @return nodes in the area @@ -65,11 +62,10 @@ public interface SpatialIndex { NodeIterable getApproximateNodesInArea(Rect2D rect); /** - * Returns the nodes in the given area using a faster, but approximate method, - * filtered by the given predicate. + * Returns the nodes in the given area using a faster, but approximate method, filtered by the given predicate. *

- * All nodes in the provided area are guaranteed to be returned, but some nodes - * outside the area may also be returned. + * All nodes in the provided area are guaranteed to be returned, but some nodes outside the area may also be + * returned. * * @param rect area to query * @param predicate filter predicate @@ -86,8 +82,7 @@ public interface SpatialIndex { EdgeIterable getEdgesInArea(Rect2D rect); /** - * Returns the edges in the given area, filtered by the given predicate. Edges - * may be returned twice. + * Returns the edges in the given area, filtered by the given predicate. Edges may be returned twice. * * @param rect area to query * @param predicate filter predicate @@ -98,8 +93,8 @@ public interface SpatialIndex { /** * Returns the edges in the given area using a faster, but approximate method. *

- * All edges in the provided area are guaranteed to be returned, but some edges - * outside the area may also be returned. Edges may also be returned twice. + * All edges in the provided area are guaranteed to be returned, but some edges outside the area may also be + * returned. Edges may also be returned twice. * * @param rect area to query * @return edges in the area @@ -107,11 +102,10 @@ public interface SpatialIndex { EdgeIterable getApproximateEdgesInArea(Rect2D rect); /** - * Returns the edges in the given area using a faster, but approximate method, - * filtered by the given predicate. + * Returns the edges in the given area using a faster, but approximate method, filtered by the given predicate. *

- * All edges in the provided area are guaranteed to be returned, but some edges - * outside the area may also be returned. Edges may also be returned twice. + * All edges in the provided area are guaranteed to be returned, but some edges outside the area may also be + * returned. Edges may also be returned twice. * * @param rect area to query * @param predicate filter predicate @@ -120,26 +114,23 @@ public interface SpatialIndex { EdgeIterable getApproximateEdgesInArea(Rect2D rect, Predicate predicate); /** - * Returns the bounding rectangle that contains all nodes in the graph. The - * boundaries are calculated based on each node's position and size. + * Returns the bounding rectangle that contains all nodes in the graph. The boundaries are calculated based on each + * node's position and size. * * @return the bounding rectangle, or null if there are no nodes */ Rect2D getBoundaries(); /** - * Acquires a read lock on the spatial index. This is recommended when using the - * query functions in a stream context, to avoid the spatial index being - * modified while being queried. + * Acquires a read lock on the spatial index. This is recommended when using the query functions in a stream + * context, to avoid the spatial index being modified while being queried. *

- * Every call to this method must be matched with a call to - * {@link #spatialIndexReadUnlock()}. + * Every call to this method must be matched with a call to {@link #spatialIndexReadUnlock()}. */ void spatialIndexReadLock(); /** - * Releases a read lock on the spatial index. This must be called after a call - * to {@link #spatialIndexReadLock()}. + * Releases a read lock on the spatial index. This must be called after a call to {@link #spatialIndexReadLock()}. */ void spatialIndexReadUnlock(); } diff --git a/src/main/java/org/gephi/graph/api/Subgraph.java b/src/main/java/org/gephi/graph/api/Subgraph.java index 8d8c4cd6..3b7671b6 100644 --- a/src/main/java/org/gephi/graph/api/Subgraph.java +++ b/src/main/java/org/gephi/graph/api/Subgraph.java @@ -20,11 +20,10 @@ /** * A subgraph is a subset of a graph based on a graph view. *

- * A subgraph has the same or less elements compared to the graph it's based on. - * This interface inherits from Graph and all read operations behave in - * a similar fashion. For instance, calling getNodes will return only - * nodes in this subgraph. However, write operations such as addNode or - * removeNode are used to control which elements are part of the view. + * A subgraph has the same or less elements compared to the graph it's based on. This interface inherits from + * Graph and all read operations behave in a similar fashion. For instance, calling getNodes will + * return only nodes in this subgraph. However, write operations such as addNode or removeNode are + * used to control which elements are part of the view. * */ public interface Subgraph extends Graph { @@ -111,8 +110,7 @@ public interface Subgraph extends Graph { public boolean removeAllNodes(Collection nodes); /** - * Retains only nodes in this subgraph that are contained in the specified - * collection. + * Retains only nodes in this subgraph that are contained in the specified collection. *

* The nodes should be part of the root graph. * @@ -144,8 +142,7 @@ public interface Subgraph extends Graph { public boolean removeAllEdges(Collection edges); /** - * Retains only edges in this subgraph that are contained in the specified - * collection. + * Retains only edges in this subgraph that are contained in the specified collection. *

* The edges should be part of the root graph. * @@ -178,8 +175,7 @@ public interface Subgraph extends Graph { public void intersection(Subgraph subGraph); /** - * Inverse this subgraph so all elements in the graph are removed and all - * elements not in the graph are added. + * Inverse this subgraph so all elements in the graph are removed and all elements not in the graph are added. */ public void not(); } diff --git a/src/main/java/org/gephi/graph/api/Table.java b/src/main/java/org/gephi/graph/api/Table.java index 7be566f1..2db59acc 100644 --- a/src/main/java/org/gephi/graph/api/Table.java +++ b/src/main/java/org/gephi/graph/api/Table.java @@ -17,8 +17,7 @@ package org.gephi.graph.api; /** - * The table is the container for columns. Column ids in all methods are - * converted to lower case. + * The table is the container for columns. Column ids in all methods are converted to lower case. */ public interface Table extends ColumnIterable { diff --git a/src/main/java/org/gephi/graph/api/TableDiff.java b/src/main/java/org/gephi/graph/api/TableDiff.java index 2ad3f944..1e29ef83 100644 --- a/src/main/java/org/gephi/graph/api/TableDiff.java +++ b/src/main/java/org/gephi/graph/api/TableDiff.java @@ -20,8 +20,8 @@ /** * Interface to retrieve added, removed and modified columns from the table. *

- * This interface is associated with a {@link TableObserver} and provides an - * easy access to the columns added or removed. + * This interface is associated with a {@link TableObserver} and provides an easy access to the columns added or + * removed. */ public interface TableDiff { diff --git a/src/main/java/org/gephi/graph/api/TableLock.java b/src/main/java/org/gephi/graph/api/TableLock.java index 52b2015f..b8b79c46 100644 --- a/src/main/java/org/gephi/graph/api/TableLock.java +++ b/src/main/java/org/gephi/graph/api/TableLock.java @@ -18,29 +18,26 @@ public interface TableLock { /** - * Acquires the lock. Acquires the lock if it is not held by another thread and - * returns immediately, setting the lock hold count to one. + * Acquires the lock. Acquires the lock if it is not held by another thread and returns immediately, setting the + * lock hold count to one. */ void lock(); /** - * Attempts to release this lock. If the current thread is the holder of this - * lock then the hold count is decremented. If the hold count is now zero then - * the lock is released. If the current thread is not the holder of this lock - * then IllegalMonitorStateException is thrown. + * Attempts to release this lock. If the current thread is the holder of this lock then the hold count is + * decremented. If the hold count is now zero then the lock is released. If the current thread is not the holder of + * this lock then IllegalMonitorStateException is thrown. * - * @throws IllegalMonitorStateException if the current thread does not hold this - * lock + * @throws IllegalMonitorStateException if the current thread does not hold this lock */ void unlock(); /** - * Queries the number of holds on this lock by the current thread. A thread has - * a hold on a lock for each lock action that is not matched by an unlock - * action. + * Queries the number of holds on this lock by the current thread. A thread has a hold on a lock for each lock + * action that is not matched by an unlock action. * - * @return the number of holds on this lock by the current thread, or zero if - * this lock is not held by the current thread + * @return the number of holds on this lock by the current thread, or zero if this lock is not held by the current + * thread */ int getHoldCount(); } diff --git a/src/main/java/org/gephi/graph/api/TableObserver.java b/src/main/java/org/gephi/graph/api/TableObserver.java index 8aab4381..bebbad5c 100644 --- a/src/main/java/org/gephi/graph/api/TableObserver.java +++ b/src/main/java/org/gephi/graph/api/TableObserver.java @@ -18,18 +18,16 @@ /** * Observer over a table to monitor changes. *

- * The table observer is a mechanism used to monitor periodically changes made - * to the table. This scenario is common in multi-threaded application where a - * thread is modifying the table and one or multiple threads need to take action - * when updates are made. + * The table observer is a mechanism used to monitor periodically changes made to the table. This scenario is common in + * multi-threaded application where a thread is modifying the table and one or multiple threads need to take action when + * updates are made. *

- * Table observer users should periodically call the - * hasTableChanged() method to check the status. Each call resets - * the observer so if the method returns true and the table doesn't change after - * that it will return false next time. + * Table observer users should periodically call the hasTableChanged() method to check the status. Each + * call resets the observer so if the method returns true and the table doesn't change after that it will return false + * next time. *

- * Observers should be destroyed when not needed anymore. A new observer can be - * obtained from the Table instance. + * Observers should be destroyed when not needed anymore. A new observer can be obtained from the Table + * instance. * * @see Table */ diff --git a/src/main/java/org/gephi/graph/api/TimeRepresentation.java b/src/main/java/org/gephi/graph/api/TimeRepresentation.java index a7201f93..af3a5b2e 100644 --- a/src/main/java/org/gephi/graph/api/TimeRepresentation.java +++ b/src/main/java/org/gephi/graph/api/TimeRepresentation.java @@ -18,15 +18,13 @@ /** * Different time representations. *

- * Both the elements (i.e nodes and edges) existence in time and the attributes' - * values in time can be represented in two different ways: using timestamps or - * using intervals. They can be mixed thought and therefore need to be + * Both the elements (i.e nodes and edges) existence in time and the attributes' values in time can be represented in + * two different ways: using timestamps or using intervals. They can be mixed thought and therefore need to be * configured by the user. *

- * Each representation has its advantages and disadvantages. For instance, - * timestamps are great when observations are made at fixed periods. On the - * other hand, intervals are great when the time is arbitrary and elements or - * attributes have long continuous existence. + * Each representation has its advantages and disadvantages. For instance, timestamps are great when observations are + * made at fixed periods. On the other hand, intervals are great when the time is arbitrary and elements or attributes + * have long continuous existence. * * @see Configuration */ @@ -34,15 +32,14 @@ public enum TimeRepresentation { /** * Timestamp representation (fixed). *

- * Time is represented using timestamps. Timestamps are single value and - * represent a single moment in time. + * Time is represented using timestamps. Timestamps are single value and represent a single moment in time. */ TIMESTAMP, /** * Interval representation (continuous). *

- * Time is represented using intervals, with a beginning and an end. Intervals - * are always included on both bounds but allows an infinite bound. + * Time is represented using intervals, with a beginning and an end. Intervals are always included on both bounds + * but allows an infinite bound. */ INTERVAL; } diff --git a/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java b/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java index 7d4980c2..3773b414 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalBooleanMap.java @@ -38,8 +38,8 @@ public IntervalBooleanMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -51,8 +51,7 @@ public IntervalBooleanMap(int capacity) { /** * Constructor with an initial interval map. *

- * The keys array must be in the same format returned by - * {@link #getIntervals() }. + * The keys array must be in the same format returned by {@link #getIntervals() }. * * @param keys initial keys content * @param vals initial values content @@ -107,8 +106,8 @@ public boolean getBoolean(Interval interval, boolean defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java b/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java index 3e922144..5bf835c7 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalByteMap.java @@ -38,8 +38,8 @@ public IntervalByteMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -51,8 +51,7 @@ public IntervalByteMap(int capacity) { /** * Constructor with an initial interval map. *

- * The keys array must be in the same format returned by - * {@link #getIntervals() }. + * The keys array must be in the same format returned by {@link #getIntervals() }. * * @param keys initial keys content * @param vals initial values content @@ -107,8 +106,8 @@ public byte getByte(Interval interval, byte defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java b/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java index 5878cc40..affcf8d1 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalCharMap.java @@ -38,8 +38,8 @@ public IntervalCharMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -51,8 +51,7 @@ public IntervalCharMap(int capacity) { /** * Constructor with an initial interval map. *

- * The keys array must be in the same format returned by - * {@link #getIntervals() }. + * The keys array must be in the same format returned by {@link #getIntervals() }. * * @param keys initial keys content * @param vals initial values content @@ -107,8 +106,8 @@ public char getCharacter(Interval interval, char defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java b/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java index 045a9893..15dd2cc9 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalDoubleMap.java @@ -38,8 +38,8 @@ public IntervalDoubleMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -51,8 +51,7 @@ public IntervalDoubleMap(int capacity) { /** * Constructor with an initial interval map. *

- * The keys array must be in the same format returned by - * {@link #getIntervals() }. + * The keys array must be in the same format returned by {@link #getIntervals() }. * * @param keys initial keys content * @param vals initial values content @@ -107,8 +106,8 @@ public double getDouble(Interval interval, double defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java b/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java index 4f24a9c1..9092f7a4 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalFloatMap.java @@ -39,8 +39,8 @@ public IntervalFloatMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -52,8 +52,7 @@ public IntervalFloatMap(int capacity) { /** * Constructor with an initial interval map. *

- * The keys array must be in the same format returned by - * {@link #getIntervals() }. + * The keys array must be in the same format returned by {@link #getIntervals() }. * * @param keys initial keys content * @param vals initial values content @@ -108,8 +107,8 @@ public float getFloat(Interval interval, float defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java b/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java index afe912be..31c12bf5 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalIntegerMap.java @@ -38,8 +38,8 @@ public IntervalIntegerMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -51,8 +51,7 @@ public IntervalIntegerMap(int capacity) { /** * Constructor with an initial interval map. *

- * The keys array must be in the same format returned by - * {@link #getIntervals() }. + * The keys array must be in the same format returned by {@link #getIntervals() }. * * @param keys initial keys content * @param vals initial values content @@ -107,8 +106,8 @@ public int getInteger(Interval interval, int defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java b/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java index 33203e1e..9126ae5a 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalLongMap.java @@ -38,8 +38,8 @@ public IntervalLongMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -51,8 +51,7 @@ public IntervalLongMap(int capacity) { /** * Constructor with an initial interval map. *

- * The keys array must be in the same format returned by - * {@link #getIntervals() }. + * The keys array must be in the same format returned by {@link #getIntervals() }. * * @param keys initial keys content * @param vals initial values content @@ -107,8 +106,8 @@ public long getLong(Interval interval, long defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalMap.java b/src/main/java/org/gephi/graph/api/types/IntervalMap.java index 10727988..8c0c026c 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalMap.java @@ -27,11 +27,10 @@ import org.gephi.graph.impl.FormattingAndParsingUtils; /** - * Abstract class that implement a sorted map between intervals and attribute - * values. + * Abstract class that implement a sorted map between intervals and attribute values. *

- * Implementations which extend this class customize the map for a unique type, - * which is represented by the T parameter. + * Implementations which extend this class customize the map for a unique type, which is represented by the + * T parameter. * * @param Value type */ @@ -52,8 +51,8 @@ public IntervalMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -310,8 +309,7 @@ public boolean isEmpty() { } /** - * Returns true if this map contains an interval that starts or ends at - * timestamp. + * Returns true if this map contains an interval that starts or ends at timestamp. * * @param timestamp timestamp * @return true if contains, false otherwise @@ -418,11 +416,11 @@ public Interval[] toKeysArray() { /** * Returns an array of all intervals in this set. *

- * The intervals are represented in a flat and sorted array (e.g. {[1.0,2.0], - * [5.0,6.0]}) returns [1.0,2.0,5.0,6.0]). + * The intervals are represented in a flat and sorted array (e.g. {[1.0,2.0], [5.0,6.0]}) returns + * [1.0,2.0,5.0,6.0]). *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all intervals */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalSet.java b/src/main/java/org/gephi/graph/api/types/IntervalSet.java index c7602efd..e1ad1147 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalSet.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalSet.java @@ -42,8 +42,8 @@ public IntervalSet() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of intervals is - * known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of intervals is known in advance as it minimizes + * array resizes. * * @param capacity interval capacity */ @@ -127,8 +127,7 @@ public Double getMinDouble() { } /** - * Returns true if this set contains an interval that starts or ends at - * timestamp. + * Returns true if this set contains an interval that starts or ends at timestamp. * * @param timestamp timestamp * @return true if contains, false otherwise @@ -167,11 +166,11 @@ public boolean contains(Interval interval) { /** * Returns an array of all intervals in this set in a flat format. *

- * The intervals are represented in a flat and sorted array (e.g. {[1.0,2.0], - * [5.0,6.0]}) returns [1.0,2.0,5.0,6.0]). + * The intervals are represented in a flat and sorted array (e.g. {[1.0,2.0], [5.0,6.0]}) returns + * [1.0,2.0,5.0,6.0]). *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all intervals */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java b/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java index 31ceb8c7..3a0dc642 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalShortMap.java @@ -38,8 +38,8 @@ public IntervalShortMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -51,8 +51,7 @@ public IntervalShortMap(int capacity) { /** * Constructor with an initial interval map. *

- * The keys array must be in the same format returned by - * {@link #getIntervals() }. + * The keys array must be in the same format returned by {@link #getIntervals() }. * * @param keys initial keys content * @param vals initial values content @@ -107,8 +106,8 @@ public short getShort(Interval interval, short defaultValue) { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java b/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java index 8b49bd30..3e03399e 100644 --- a/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java +++ b/src/main/java/org/gephi/graph/api/types/IntervalStringMap.java @@ -37,8 +37,8 @@ public IntervalStringMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -50,8 +50,7 @@ public IntervalStringMap(int capacity) { /** * Constructor with an initial interval map. *

- * The keys array must be in the same format returned by - * {@link #getIntervals() }. + * The keys array must be in the same format returned by {@link #getIntervals() }. * * @param keys initial keys content * @param vals initial values content diff --git a/src/main/java/org/gephi/graph/api/types/TimeMap.java b/src/main/java/org/gephi/graph/api/types/TimeMap.java index 4f33d205..ec5298be 100644 --- a/src/main/java/org/gephi/graph/api/types/TimeMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimeMap.java @@ -21,8 +21,7 @@ import org.gephi.graph.api.TimeFormat; /** - * Interface that defines the functionalities both timestamp and interval map - * have. + * Interface that defines the functionalities both timestamp and interval map have. * * @param key type * @param value type @@ -49,8 +48,8 @@ public interface TimeMap { /** * Get the estimated value for the given interval. *

- * The estimator is used to determine the way multiple interval values are - * merged together (e.g average, first, median). + * The estimator is used to determine the way multiple interval values are merged together (e.g average, first, + * median). * * @param interval interval query * @param estimator estimator used diff --git a/src/main/java/org/gephi/graph/api/types/TimeSet.java b/src/main/java/org/gephi/graph/api/types/TimeSet.java index 2a01d03e..d2393cc0 100644 --- a/src/main/java/org/gephi/graph/api/types/TimeSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimeSet.java @@ -19,8 +19,7 @@ import org.gephi.graph.api.TimeFormat; /** - * Interface that defines the functionalities both timestamp and interval set - * have. + * Interface that defines the functionalities both timestamp and interval set have. * * @param key type */ @@ -95,16 +94,16 @@ public interface TimeSet { /** * Returns an array of all keys in this set. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all keys */ public K[] toArray(); /** - * Returns the same result as {@link #toArray() } but in a primitive array if - * the underlying storage is in a primitive form. + * Returns the same result as {@link #toArray() } but in a primitive array if the underlying storage is in a + * primitive form. * * @return array of all keys */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java b/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java index 9b7a73e5..4162e426 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampBooleanMap.java @@ -39,8 +39,8 @@ public TimestampBooleanMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -166,8 +166,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java b/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java index d5afaa18..de11da08 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampByteMap.java @@ -38,8 +38,8 @@ public TimestampByteMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -129,8 +129,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java b/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java index 2e368062..23685cfd 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampCharMap.java @@ -39,8 +39,8 @@ public TimestampCharMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -162,8 +162,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java b/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java index 8c10ad0b..491cab98 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampDoubleMap.java @@ -37,8 +37,8 @@ public TimestampDoubleMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -110,8 +110,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java b/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java index 73998d58..c0522f3b 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampFloatMap.java @@ -39,8 +39,8 @@ public TimestampFloatMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -130,8 +130,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java b/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java index 873c9dac..404cb2ca 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampIntegerMap.java @@ -38,8 +38,8 @@ public TimestampIntegerMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -123,8 +123,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java b/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java index 33a6e8ec..1f3905fd 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampLongMap.java @@ -38,8 +38,8 @@ public TimestampLongMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -123,8 +123,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampMap.java b/src/main/java/org/gephi/graph/api/types/TimestampMap.java index 9af6613b..edbb5c98 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampMap.java @@ -27,11 +27,10 @@ import org.gephi.graph.impl.FormattingAndParsingUtils; /** - * Abstract class that implement a sorted map between timestamp and attribute - * values. + * Abstract class that implement a sorted map between timestamp and attribute values. *

- * Implementations which extend this class customize the map for a unique type, - * which is represented by the T parameter. + * Implementations which extend this class customize the map for a unique type, which is represented by the + * T parameter. * * @param Value type */ @@ -52,8 +51,8 @@ public TimestampMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -267,8 +266,8 @@ public boolean contains(Double timestamp) { /** * Returns an array of all timestamps in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all timestamps */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampSet.java b/src/main/java/org/gephi/graph/api/types/TimestampSet.java index 4a3c6441..281cf283 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampSet.java @@ -41,8 +41,8 @@ public TimestampSet() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java b/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java index 5d5eb2e4..0c5b3a2e 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampShortMap.java @@ -38,8 +38,8 @@ public TimestampShortMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ @@ -123,8 +123,8 @@ public Class getTypeClass() { /** * Returns an array of all values in this map. *

- * This method may return a reference to the underlying array so clients should - * make a copy if the array is written to. + * This method may return a reference to the underlying array so clients should make a copy if the array is written + * to. * * @return array of all values */ diff --git a/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java b/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java index 0e3566c0..26988049 100644 --- a/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimestampStringMap.java @@ -37,8 +37,8 @@ public TimestampStringMap() { /** * Constructor with capacity. *

- * Using this constructor can improve performances if the number of timestamps - * is known in advance as it minimizes array resizes. + * Using this constructor can improve performances if the number of timestamps is known in advance as it minimizes + * array resizes. * * @param capacity timestamp capacity */ diff --git a/src/main/java/org/gephi/graph/impl/ArraysParser.java b/src/main/java/org/gephi/graph/impl/ArraysParser.java index 0a171f91..bdbf19be 100644 --- a/src/main/java/org/gephi/graph/impl/ArraysParser.java +++ b/src/main/java/org/gephi/graph/impl/ArraysParser.java @@ -129,13 +129,11 @@ public static T[] parseArray(Class arrayTypeClass, String input) throws /** * Parses an array of any primitive type. * - * @param Primitive type wrapper. For example Integer for int array or Long - * for long array. + * @param Primitive type wrapper. For example Integer for int array or Long for long array. * @param arrayTypeClass Array type to parse * @param input Input string to parse * @return Parsed array - * @throws IllegalArgumentException Parsing exception, or if any of the parsed - * array values is null + * @throws IllegalArgumentException Parsing exception, or if any of the parsed array values is null */ public static Object parseArrayAsPrimitiveArray(Class arrayTypeClass, String input) throws IllegalArgumentException { T[] array = parseArray(arrayTypeClass, input); diff --git a/src/main/java/org/gephi/graph/impl/EdgeStore.java b/src/main/java/org/gephi/graph/impl/EdgeStore.java index f9912666..730003ca 100644 --- a/src/main/java/org/gephi/graph/impl/EdgeStore.java +++ b/src/main/java/org/gephi/graph/impl/EdgeStore.java @@ -1499,8 +1499,8 @@ public boolean hasNext() { } /** - * Abstract base class for iterating over edges connected to nodes. Provides - * common logic for handling both incoming and outgoing edges. + * Abstract base class for iterating over edges connected to nodes. Provides common logic for handling both incoming + * and outgoing edges. */ protected abstract class AbstractEdgeInOutIterator implements Iterator { @@ -1522,8 +1522,7 @@ protected AbstractEdgeInOutIterator(boolean locking) { } /** - * Initialize arrays for the current node. Called when starting iteration for a - * new node. + * Initialize arrays for the current node. Called when starting iteration for a new node. */ protected void initializeForNode(NodeImpl node) { outArray = node.headOut; @@ -1536,8 +1535,8 @@ protected void initializeForNode(NodeImpl node) { } /** - * Called when the current node has no more edges. Should return true if there - * are more nodes to process, false otherwise. + * Called when the current node has no more edges. Should return true if there are more nodes to process, false + * otherwise. */ protected abstract boolean moveToNextNode(); @@ -1633,9 +1632,8 @@ protected boolean moveToNextNode() { } /** - * Iterator for edges connected to multiple nodes (both incoming and outgoing). - * Iterates through all edges of all provided nodes without creating separate - * iterators. + * Iterator for edges connected to multiple nodes (both incoming and outgoing). Iterates through all edges of all + * provided nodes without creating separate iterators. */ protected final class EdgeInOutMultiIterator extends AbstractEdgeInOutIterator { diff --git a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java index fc6d52fe..28186904 100644 --- a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java +++ b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java @@ -25,8 +25,7 @@ import org.gephi.graph.api.AttributeUtils; /** - * Utils for formatting and parsing special data types (dynamic intervals, - * timestamps and arrays). + * Utils for formatting and parsing special data types (dynamic intervals, timestamps and arrays). * * @author Eduardo Ramos */ @@ -45,8 +44,8 @@ public final class FormattingAndParsingUtils { public static final String INFINITY = "Infinity"; /** - * Parses an ISO date with or without time or a timestamp (in milliseconds). - * Returns the date or timestamp converted to a timestamp in milliseconds. + * Parses an ISO date with or without time or a timestamp (in milliseconds). Returns the date or timestamp converted + * to a timestamp in milliseconds. * * @param timeStr Date or timestamp string * @param zoneId Time zone to use or null to use default time zone (UTC) @@ -69,9 +68,8 @@ public static double parseDateTimeOrTimestamp(String timeStr, ZoneId zoneId) thr } /** - * Parses an ISO date with or without time or a timestamp (in milliseconds). - * Returns the date or timestamp converted to a timestamp in milliseconds. - * Default time zone is used (UTC). + * Parses an ISO date with or without time or a timestamp (in milliseconds). Returns the date or timestamp converted + * to a timestamp in milliseconds. Default time zone is used (UTC). * * @param timeStr Date or timestamp string * @return Timestamp @@ -128,8 +126,7 @@ protected static String parseLiteral(StringReader reader, char quote) throws IOE } /** - * Parses a value until end is detected either by a comma or a bounds closing - * character. + * Parses a value until end is detected either by a comma or a bounds closing character. * * @param reader Input reader * @return Parsed value @@ -157,9 +154,8 @@ protected static String parseValue(StringReader reader) throws IOException { } /** - * Converts a string parsed with {@link #parseValue(java.io.StringReader)} to - * the target type, taking into account dynamic parsing quirks such as numbers - * with/without decimals and infinity values. + * Converts a string parsed with {@link #parseValue(java.io.StringReader)} to the target type, taking into account + * dynamic parsing quirks such as numbers with/without decimals and infinity values. * * @param Target type * @param typeClass Target type class @@ -216,9 +212,8 @@ private static T parseNumberWithDecimals(Class typeClass, } /** - * Removes anything after the dot of decimal numbers in a string when necessary. - * Used for trying to parse decimal numbers as not decimal. For example - * BigDecimal to BigInteger. + * Removes anything after the dot of decimal numbers in a string when necessary. Used for trying to parse decimal + * numbers as not decimal. For example BigDecimal to BigInteger. * * @param s String to remove decimal digits * @return String without dot and decimal digits. @@ -236,8 +231,7 @@ private static String removeDecimalDigitsFromString(String s) { /** * @param value String value - * @return True if the string contains special characters for dynamic types - * intervals syntax + * @return True if the string contains special characters for dynamic types intervals syntax */ public static boolean containsDynamicSpecialCharacters(String value) { for (char c : DYNAMIC_SPECIAL_CHARACTERS) { @@ -289,8 +283,7 @@ public static String printArray(Object arr) { /** * @param value String value - * @return True if the string contains special characters for arrays intervals - * syntax + * @return True if the string contains special characters for arrays intervals syntax */ private static boolean containsArraySpecialCharacters(String value) { for (char c : ARRAY_SPECIAL_CHARACTERS) { diff --git a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java index 168c9a5e..b4b6f885 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewImpl.java @@ -667,8 +667,8 @@ public void addEdgeInNodeView(EdgeImpl edge) { } /** - * Bulk remove nodes from the view. This is more efficient than removing nodes - * one by one as it batches index updates and increments version only once. + * Bulk remove nodes from the view. This is more efficient than removing nodes one by one as it batches index + * updates and increments version only once. */ private void bulkRemoveNodes(BitSet nodesToRemove) { // First pass: collect all edges to remove (incident to removed nodes) @@ -720,8 +720,8 @@ private void bulkRemoveNodes(BitSet nodesToRemove) { } /** - * Bulk add nodes to the view. This is more efficient than adding nodes one by - * one as it batches index updates and increments version only once. + * Bulk add nodes to the view. This is more efficient than adding nodes one by one as it batches index updates and + * increments version only once. */ private void bulkAddNodes(BitSet nodesToAdd) { // Update node bit vector @@ -776,8 +776,8 @@ private void bulkAddNodes(BitSet nodesToAdd) { } /** - * Bulk remove edges from the view. This is more efficient than removing edges - * one by one as it updates stats in bulk and increments version only once. + * Bulk remove edges from the view. This is more efficient than removing edges one by one as it updates stats in + * bulk and increments version only once. */ private void bulkRemoveEdges(BitSet edgesToRemove) { // Update edge bit vector @@ -827,8 +827,8 @@ private void bulkRemoveEdges(BitSet edgesToRemove) { } /** - * Bulk add edges to the view. This is more efficient than adding edges one by - * one as it updates stats in bulk and increments version only once. + * Bulk add edges to the view. This is more efficient than adding edges one by one as it updates stats in bulk and + * increments version only once. */ private void bulkAddEdges(BitSet edgesToAdd) { // Update edge bit vector @@ -878,9 +878,8 @@ private void bulkAddEdges(BitSet edgesToAdd) { } /** - * Special bulk remove for the not() operation. This updates the bit vector and - * stats but does NOT increment version or update indexes (those are handled - * separately in not()). + * Special bulk remove for the not() operation. This updates the bit vector and stats but does NOT increment version + * or update indexes (those are handled separately in not()). */ private void bulkRemoveEdgesForNot(BitSet edgesToRemove) { // Update edge bit vector diff --git a/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java b/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java index cde9af3e..61a50982 100644 --- a/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java +++ b/src/main/java/org/gephi/graph/impl/Interval2IntTreeMap.java @@ -379,8 +379,7 @@ private Node succesor(Node x) { /** * Returns the interval with the lowest left endpoint. * - * @return the interval with the lowest left endpoint or null if the tree is - * empty. + * @return the interval with the lowest left endpoint or null if the tree is empty. */ public Interval minimum() { if (root.left == nil) { @@ -410,8 +409,7 @@ private Interval treeMinimum(Node x) { /** * Returns the interval with the highest right endpoint. * - * @return the interval with the highest right endpoint or null if the tree is - * empty. + * @return the interval with the highest right endpoint or null if the tree is empty. */ public Interval maximum() { if (root.left == nil) { @@ -430,8 +428,7 @@ private Node treeMaximum(Node x) { } /** - * Returns the leftmost point or {@code Double.NEGATIVE_INFINITY} in case of no - * intervals. + * Returns the leftmost point or {@code Double.NEGATIVE_INFINITY} in case of no intervals. * * @return the leftmost point */ @@ -447,8 +444,7 @@ public double getLow() { } /** - * Returns the rightmost point or {@code Double.POSITIVE_INFINITY} in case of no - * intervals. + * Returns the rightmost point or {@code Double.POSITIVE_INFINITY} in case of no intervals. * * @return the rightmost point */ @@ -509,8 +505,7 @@ public Set> entrySet() { } /** - * Returns an entry set of all entries, which interval keys overlap with - * point. + * Returns an entry set of all entries, which interval keys overlap with point. * * @param point point * @return entry set @@ -520,8 +515,7 @@ public Set> entrySet(double point) { } /** - * Returns an entry set of all entries, which interval keys overlap with - * interval. + * Returns an entry set of all entries, which interval keys overlap with interval. * * @param interval interval * @return entry set @@ -659,9 +653,8 @@ private void inorderTreeWalk(Node x, List list) { * * @param obj object to which this interval tree is to be compared * - * @return {@code true} if and only if the specified {@code Object} is a - * {@code IntervalTree} which contain the same intervals as this - * {@code IntervalTree's}. + * @return {@code true} if and only if the specified {@code Object} is a {@code IntervalTree} which contain the same + * intervals as this {@code IntervalTree's}. * * @see #hashCode */ diff --git a/src/main/java/org/gephi/graph/impl/IntervalsParser.java b/src/main/java/org/gephi/graph/impl/IntervalsParser.java index 43064210..6ee12828 100644 --- a/src/main/java/org/gephi/graph/impl/IntervalsParser.java +++ b/src/main/java/org/gephi/graph/impl/IntervalsParser.java @@ -48,18 +48,16 @@ *

* *

- * The standard format for {@link IntervalMap} is <[start, end, value1]; - * [start, end, value2]>. + * The standard format for {@link IntervalMap} is <[start, end, value1]; [start, end, value2]>. *

* *

- * The standard format for {@link IntervalSet} is <[start, end]; [start, - * end]>. + * The standard format for {@link IntervalSet} is <[start, end]; [start, end]>. *

* *

- * Start and end values can be both numbers and ISO dates or datetimes. Dates - * and datetimes will be converted to their millisecond-precision timestamp. + * Start and end values can be both numbers and ISO dates or datetimes. Dates and datetimes will be converted to their + * millisecond-precision timestamp. *

* * Examples of valid interval maps are: @@ -77,14 +75,12 @@ * * *

- * All open intervals will be converted to closed intervals, as only - * closed intervals are supported. + * All open intervals will be converted to closed intervals, as only closed intervals are supported. *

* *

- * The most correct examples are those that include < > and proper commas - * and semicolons for separation, but the parser will be indulgent when - * possible. + * The most correct examples are those that include < > and proper commas and semicolons for separation, but the + * parser will be indulgent when possible. *

* * @author Eduardo Ramos @@ -96,11 +92,9 @@ public final class IntervalsParser { * * @param input Input string to parse * @param zoneId Time zone to use or null to use default time zone (UTC) - * @return Resulting {@link IntervalSet}, or null if the input equals - * '<empty>' or is null - * @throws IllegalArgumentException Thrown if there are no intervals in the - * input string or bounds cannot be parsed into doubles or - * dates/datetimes. + * @return Resulting {@link IntervalSet}, or null if the input equals '<empty>' or is null + * @throws IllegalArgumentException Thrown if there are no intervals in the input string or bounds cannot be parsed + * into doubles or dates/datetimes. */ public static IntervalSet parseIntervalSet(String input, ZoneId zoneId) throws IllegalArgumentException { if (input == null) { @@ -127,34 +121,27 @@ public static IntervalSet parseIntervalSet(String input, ZoneId zoneId) throws I } /** - * Parses a {@link IntervalSet} type with one or more intervals. Default time - * zone is used (UTC). + * Parses a {@link IntervalSet} type with one or more intervals. Default time zone is used (UTC). * * @param input Input string to parse - * @return Resulting {@link IntervalSet}, or null if the input equals - * '<empty>' or is null - * @throws IllegalArgumentException Thrown if there are no intervals in the - * input string or bounds cannot be parsed into doubles or - * dates/datetimes. + * @return Resulting {@link IntervalSet}, or null if the input equals '<empty>' or is null + * @throws IllegalArgumentException Thrown if there are no intervals in the input string or bounds cannot be parsed + * into doubles or dates/datetimes. */ public static IntervalSet parseIntervalSet(String input) throws IllegalArgumentException { return parseIntervalSet(input, null); } /** - * Parses a {@link IntervalMap} type with one or more intervals, and their - * associated values. + * Parses a {@link IntervalMap} type with one or more intervals, and their associated values. * * @param Underlying type of the {@link IntervalMap} values - * @param typeClass Simple type or {@link IntervalMap} subtype for the result - * intervals' values. + * @param typeClass Simple type or {@link IntervalMap} subtype for the result intervals' values. * @param input Input string to parse * @param zoneId Time zone to use or null to use default time zone (UTC) - * @return Resulting {@link IntervalMap}, or null if the input equals - * '<empty>' or is null - * @throws IllegalArgumentException Thrown if type class is not supported, any - * of the intervals don't have a value or have an invalid value, there - * are no intervals in the input string or bounds cannot be parsed into + * @return Resulting {@link IntervalMap}, or null if the input equals '<empty>' or is null + * @throws IllegalArgumentException Thrown if type class is not supported, any of the intervals don't have a value + * or have an invalid value, there are no intervals in the input string or bounds cannot be parsed into * doubles or dates/datetimes. */ public static IntervalMap parseIntervalMap(Class typeClass, String input, ZoneId zoneId) throws IllegalArgumentException { @@ -210,18 +197,15 @@ public static IntervalMap parseIntervalMap(Class typeClass, String inp } /** - * Parses a {@link IntervalMap} type with one or more intervals, and their - * associated values. Default time zone is used (UTC). + * Parses a {@link IntervalMap} type with one or more intervals, and their associated values. Default time zone is + * used (UTC). * * @param Underlying type of the {@link IntervalMap} values - * @param typeClass Simple type or {@link IntervalMap} subtype for the result - * intervals' values. + * @param typeClass Simple type or {@link IntervalMap} subtype for the result intervals' values. * @param input Input string to parse - * @return Resulting {@link IntervalMap}, or null if the input equals - * '<empty>' or is null - * @throws IllegalArgumentException Thrown if type class is not supported, any - * of the intervals don't have a value or have an invalid value, there - * are no intervals in the input string or bounds cannot be parsed into + * @return Resulting {@link IntervalMap}, or null if the input equals '<empty>' or is null + * @throws IllegalArgumentException Thrown if type class is not supported, any of the intervals don't have a value + * or have an invalid value, there are no intervals in the input string or bounds cannot be parsed into * doubles or dates/datetimes. */ public static IntervalMap parseIntervalMap(Class typeClass, String input) throws IllegalArgumentException { @@ -229,12 +213,10 @@ public static IntervalMap parseIntervalMap(Class typeClass, String inp } /** - * Parses intervals with values (of {@code typeClass} Class) or without values - * (null {@code typeClass} Class) + * Parses intervals with values (of {@code typeClass} Class) or without values (null {@code typeClass} Class) * * @param Type of the interval value - * @param typeClass Class of the intervals' values or null to parse intervals - * without values + * @param typeClass Class of the intervals' values or null to parse intervals without values * @param input Input to parse * @param zoneId Time zone to use or null to use default time zone (UTC) * @return List of Interval @@ -339,8 +321,7 @@ private static IntervalWithValue buildInterval(Class typeClass, ArrayL } /** - * Represents an Interval with an associated value for it. Only for internal - * usage in this class. + * Represents an Interval with an associated value for it. Only for internal usage in this class. * * @author Eduardo Ramos * @param Type of the value diff --git a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java index a0996d64..82b152f9 100644 --- a/src/main/java/org/gephi/graph/impl/NodesQuadTree.java +++ b/src/main/java/org/gephi/graph/impl/NodesQuadTree.java @@ -1600,9 +1600,8 @@ protected boolean testPredicate(Edge element) { } /** - * A spliterator that iterates through all edges in the EdgeStore and filters - * them based on whether their nodes belong to quad tree nodes that overlap with - * a search rectangle. This approach iterates edges directly rather than + * A spliterator that iterates through all edges in the EdgeStore and filters them based on whether their nodes + * belong to quad tree nodes that overlap with a search rectangle. This approach iterates edges directly rather than * iterating nodes first. */ protected class QuadTreeGlobalEdgesSpliterator implements Spliterator { diff --git a/src/main/java/org/gephi/graph/impl/TimestampsParser.java b/src/main/java/org/gephi/graph/impl/TimestampsParser.java index 5e4dcac1..2362304c 100644 --- a/src/main/java/org/gephi/graph/impl/TimestampsParser.java +++ b/src/main/java/org/gephi/graph/impl/TimestampsParser.java @@ -48,18 +48,16 @@ *

* *

- * The standard format for {@link TimestampMap} is <[timestamp, value1]; - * [timestamp, value2]>. + * The standard format for {@link TimestampMap} is <[timestamp, value1]; [timestamp, value2]>. *

* *

- * The standard format for {@link TimestampSet} is <[timestamp1, timestamp2, - * timestamp3, ...]>. + * The standard format for {@link TimestampSet} is <[timestamp1, timestamp2, timestamp3, ...]>. *

* *

- * Timestamps values can be both numbers and ISO dates or datetimes. Dates and - * datetimes will be converted to their millisecond-precision timestamp. + * Timestamps values can be both numbers and ISO dates or datetimes. Dates and datetimes will be converted to their + * millisecond-precision timestamp. *

* * Examples of valid timestamp maps are: @@ -77,9 +75,8 @@ * * *

- * The most correct examples are those that include < > and proper commas - * and semicolons for separation, but the parser will be indulgent when - * possible. + * The most correct examples are those that include < > and proper commas and semicolons for separation, but the + * parser will be indulgent when possible. *

* * @author Eduardo Ramos @@ -91,11 +88,9 @@ public final class TimestampsParser { * * @param input Input string to parse * @param zoneId Time zone to use or null to use default time zone (UTC) - * @return Resulting {@link TimestampSet}, or null if the input equals - * '<empty>' or is null - * @throws IllegalArgumentException Thrown if there are no timestamps in the - * input string or bounds cannot be parsed into doubles or - * dates/datetimes. + * @return Resulting {@link TimestampSet}, or null if the input equals '<empty>' or is null + * @throws IllegalArgumentException Thrown if there are no timestamps in the input string or bounds cannot be parsed + * into doubles or dates/datetimes. */ public static TimestampSet parseTimestampSet(String input, ZoneId zoneId) throws IllegalArgumentException { if (input == null) { @@ -165,34 +160,27 @@ public static TimestampSet parseTimestampSet(String input, ZoneId zoneId) throws } /** - * Parses a {@link TimestampSet} type with one or more timestamps. Default time - * zone is used (UTC). + * Parses a {@link TimestampSet} type with one or more timestamps. Default time zone is used (UTC). * * @param input Input string to parse - * @return Resulting {@link TimestampSet}, or null if the input equals - * '<empty>' or is null - * @throws IllegalArgumentException Thrown if there are no timestamps in the - * input string or bounds cannot be parsed into doubles or - * dates/datetimes. + * @return Resulting {@link TimestampSet}, or null if the input equals '<empty>' or is null + * @throws IllegalArgumentException Thrown if there are no timestamps in the input string or bounds cannot be parsed + * into doubles or dates/datetimes. */ public static TimestampSet parseTimestampSet(String input) throws IllegalArgumentException { return parseTimestampSet(input, null); } /** - * Parses a {@link TimestampMap} type with one or more timestamps, and their - * associated values. + * Parses a {@link TimestampMap} type with one or more timestamps, and their associated values. * * @param Underlying type of the {@link TimestampMap} values - * @param typeClass Simple type or {@link TimestampMap} subtype for the result - * values. + * @param typeClass Simple type or {@link TimestampMap} subtype for the result values. * @param input Input string to parse * @param zoneId Time zone to use or null to use default time zone (UTC) - * @return Resulting {@link TimestampMap}, or null if the input equals - * '<empty>' or is null - * @throws IllegalArgumentException Thrown if type class is not supported, any - * of the timestamps don't have a value or have an invalid value, there - * are no timestamps in the input string or bounds cannot be parsed into + * @return Resulting {@link TimestampMap}, or null if the input equals '<empty>' or is null + * @throws IllegalArgumentException Thrown if type class is not supported, any of the timestamps don't have a value + * or have an invalid value, there are no timestamps in the input string or bounds cannot be parsed into * doubles or dates/datetimes. */ public static TimestampMap parseTimestampMap(Class typeClass, String input, ZoneId zoneId) throws IllegalArgumentException { @@ -263,18 +251,15 @@ public static TimestampMap parseTimestampMap(Class typeClass, String i } /** - * Parses a {@link TimestampMap} type with one or more timestamps, and their - * associated values. Default time zone is used (UTC). + * Parses a {@link TimestampMap} type with one or more timestamps, and their associated values. Default time zone is + * used (UTC). * * @param Underlying type of the {@link TimestampMap} values - * @param typeClass Simple type or {@link TimestampMap} subtype for the result - * values. + * @param typeClass Simple type or {@link TimestampMap} subtype for the result values. * @param input Input string to parse - * @return Resulting {@link TimestampMap}, or null if the input equals - * '<empty>' or is null - * @throws IllegalArgumentException Thrown if type class is not supported, any - * of the timestamps don't have a value or have an invalid value, there - * are no timestamps in the input string or bounds cannot be parsed into + * @return Resulting {@link TimestampMap}, or null if the input equals '<empty>' or is null + * @throws IllegalArgumentException Thrown if type class is not supported, any of the timestamps don't have a value + * or have an invalid value, there are no timestamps in the input string or bounds cannot be parsed into * doubles or dates/datetimes. */ public static TimestampMap parseTimestampMap(Class typeClass, String input) throws IllegalArgumentException { diff --git a/src/main/java/org/gephi/graph/impl/utils/LongPacker.java b/src/main/java/org/gephi/graph/impl/utils/LongPacker.java index cb7eb489..0d1a7b92 100644 --- a/src/main/java/org/gephi/graph/impl/utils/LongPacker.java +++ b/src/main/java/org/gephi/graph/impl/utils/LongPacker.java @@ -21,11 +21,9 @@ import java.nio.ByteBuffer; /** - * Packing utility for non-negative long and int - * values. + * Packing utility for non-negative long and int values. *

- * Originally developed for Kryo by Nathan Sweet. Modified for JDBM by Jan - * Kotek. + * Originally developed for Kryo by Nathan Sweet. Modified for JDBM by Jan Kotek. */ public final class LongPacker { @@ -35,8 +33,8 @@ private LongPacker() { } /** - * Pack non-negative long into output stream. It will occupy 1-10 bytes - * depending on value (lower values occupy smaller space) + * Pack non-negative long into output stream. It will occupy 1-10 bytes depending on value (lower values occupy + * smaller space) * * @param os the data output * @param value the long value @@ -60,8 +58,8 @@ static public int packLong(DataOutput os, long value) throws IOException { } /** - * Pack non-negative long into byte array. It will occupy 1-10 bytes depending - * on value (lower values occupy smaller space) + * Pack non-negative long into byte array. It will occupy 1-10 bytes depending on value (lower values occupy smaller + * space) * * @param ba the byte array * @param value the long value @@ -136,8 +134,8 @@ static public long unpackLong(byte[] ba, int index) { } /** - * Pack non-negative int into output stream. It will occupy 1-5 bytes depending - * on value (lower values occupy smaller space) + * Pack non-negative int into output stream. It will occupy 1-5 bytes depending on value (lower values occupy + * smaller space) * * @param os the data output * @param value the value diff --git a/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java b/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java index 642e2deb..7c1e8120 100644 --- a/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java +++ b/src/main/java/org/gephi/graph/impl/utils/MapDeepEquals.java @@ -27,9 +27,8 @@ private MapDeepEquals() { } /** - * Compares two maps for equality. This is based around the idea that if the - * keys are deep equal and the values the keys return are deep equal then the - * maps are equal. + * Compares two maps for equality. This is based around the idea that if the keys are deep equal and the values the + * keys return are deep equal then the maps are equal. * * @param m1 - first map * @param m2 - second map diff --git a/src/main/java/org/gephi/graph/spi/LayoutData.java b/src/main/java/org/gephi/graph/spi/LayoutData.java index 91e54ce7..32c11bce 100644 --- a/src/main/java/org/gephi/graph/spi/LayoutData.java +++ b/src/main/java/org/gephi/graph/spi/LayoutData.java @@ -18,12 +18,10 @@ import org.gephi.graph.api.Node; /** - * Interface for node metadata to handle custom layout attributes more - * efficiently. + * Interface for node metadata to handle custom layout attributes more efficiently. *

* Layout implementations can implement this interface and use the - * {@link Node#setLayoutData(org.gephi.graph.spi.LayoutData) } method to - * associate any metadata with the node. + * {@link Node#setLayoutData(org.gephi.graph.spi.LayoutData) } method to associate any metadata with the node. */ public interface LayoutData { } From dcf8931405b59ad8a63356fb6c83e262355f89bc Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 9 May 2026 09:41:53 +0200 Subject: [PATCH 252/271] Make GraphStore.removeNode silent on already-removed nodes #274 --- .../java/org/gephi/graph/impl/GraphStore.java | 6 ++++ .../org/gephi/graph/impl/GraphStoreTest.java | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 65bbbcc6..885e694a 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -277,6 +277,9 @@ public boolean removeNode(final Node node) { autoWriteLock(); try { nodeStore.checkNonNullNodeObject(node); + if (((NodeImpl) node).storeId == NodeStore.NULL_ID) { + return false; + } for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(node, false); edgeIterator .hasNext();) { edgeIterator.next(); @@ -304,6 +307,9 @@ public boolean removeAllNodes(Collection nodes) { try { for (Node node : nodes) { nodeStore.checkNonNullNodeObject(node); + if (((NodeImpl) node).storeId == NodeStore.NULL_ID) { + continue; + } for (EdgeStore.EdgeInOutIterator edgeIterator = edgeStore.edgeIterator(node, false); edgeIterator .hasNext();) { edgeIterator.next(); diff --git a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java index 51a1d4a4..b08abda0 100644 --- a/src/test/java/org/gephi/graph/impl/GraphStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphStoreTest.java @@ -799,6 +799,34 @@ public void testRemoveEdge() { Assert.assertFalse(graphStore.removeEdge(edge)); } + @Test + public void testRemoveNodeAlreadyRemoved() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + Node node = graphStore.getNodes().toArray()[0]; + + Assert.assertTrue(graphStore.removeNode(node)); + Assert.assertFalse(graphStore.contains(node)); + Assert.assertFalse(graphStore.removeNode(node)); + } + + @Test + public void testRemoveNodeNeverAdded() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + Node detached = graphStore.factory.newNode("detached"); + + Assert.assertFalse(graphStore.removeNode(detached)); + } + + @Test + public void testRemoveAllNodesWithAlreadyRemoved() { + GraphStore graphStore = GraphGenerator.generateSmallGraphStore(); + Node[] nodes = graphStore.getNodes().toArray(); + + Assert.assertTrue(graphStore.removeNode(nodes[0])); + Assert.assertTrue(graphStore.removeAllNodes(Arrays.asList(nodes))); + Assert.assertEquals(graphStore.getNodeCount(), 0); + } + @Test public void testRemoveAllNode() { GraphStore graphStore = GraphGenerator.generateSmallMultiTypeGraphStore(); From bd4095733dc6fa94b7ba723821cfac3d76d7c08b Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 9 May 2026 09:48:11 +0200 Subject: [PATCH 253/271] Set version to 0.8.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff4e0a74..692197c2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.6-SNAPSHOT + 0.8.6 jar GraphStore From 1682262e712e61eb3de000783b45909d02b35f44 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 9 May 2026 09:50:29 +0200 Subject: [PATCH 254/271] Set version to 0.8.7-SNAPSHOT --- README.md | 4 ++-- pom.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e613ead2..8a96095a 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.8.5 + 0.8.6 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.8.5' +compile 'org.gephi:graphstore:0.8.6' ``` ## Dependencies diff --git a/pom.xml b/pom.xml index 692197c2..3d8d4b7a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.6 + 0.8.7-SNAPSHOT jar GraphStore From 3ab6b085a731a48a075fbd2ac660e492a47c7f58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 09:07:15 +0200 Subject: [PATCH 255/271] Bump org.apache.maven.plugins:maven-surefire-plugin from 3.5.5 to 3.5.6 (#275) Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.5.5 to 3.5.6. - [Release notes](https://github.com/apache/maven-surefire/releases) - [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.5.5...surefire-3.5.6) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-surefire-plugin dependency-version: 3.5.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3d8d4b7a..836569f1 100644 --- a/pom.xml +++ b/pom.xml @@ -80,7 +80,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.5 + 3.5.6 org.apache.maven.plugins From c6529204b0a5855a19c17b45f58e5b8a0903cb87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Jun 2026 17:56:20 +0200 Subject: [PATCH 256/271] Bump org.sonatype.central:central-publishing-maven-plugin (#281) Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.10.0 to 0.11.0. - [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits) --- updated-dependencies: - dependency-name: org.sonatype.central:central-publishing-maven-plugin dependency-version: 0.11.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 836569f1..7af66903 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ org.sonatype.central central-publishing-maven-plugin - 0.10.0 + 0.11.0 true From f337d6572f367481a6e4dc403bed52f2b5a3b7f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Jun 2026 21:29:31 +0200 Subject: [PATCH 257/271] Bump actions/checkout from 6 to 7 (#280) Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd07f9ce..8b456aa1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Set up Maven Central Repository uses: actions/setup-java@v5 with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a1ffefbf..918883b3 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -7,7 +7,7 @@ jobs: build_and_test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Set up JDK 17 uses: actions/setup-java@v5 with: From 7c9ac1b2763ea52b5bc665e0f5c0ce861d9e071c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 28 Jun 2026 21:29:44 +0200 Subject: [PATCH 258/271] Bump org.jacoco:jacoco-maven-plugin from 0.8.14 to 0.8.15 (#276) Bumps [org.jacoco:jacoco-maven-plugin](https://github.com/jacoco/jacoco) from 0.8.14 to 0.8.15. - [Release notes](https://github.com/jacoco/jacoco/releases) - [Commits](https://github.com/jacoco/jacoco/compare/v0.8.14...v0.8.15) --- updated-dependencies: - dependency-name: org.jacoco:jacoco-maven-plugin dependency-version: 0.8.15 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7af66903..f3c94730 100644 --- a/pom.xml +++ b/pom.xml @@ -106,7 +106,7 @@ org.jacoco jacoco-maven-plugin - 0.8.14 + 0.8.15 org.eluder.coveralls From 4665778e0850e66a177298199d90b51efed757e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 16 Aug 2026 17:40:01 +0200 Subject: [PATCH 259/271] Bump it.unimi.dsi:fastutil from 8.5.18 to 8.5.19 (#283) Bumps [it.unimi.dsi:fastutil](https://github.com/vigna/fastutil) from 8.5.18 to 8.5.19. - [Changelog](https://github.com/vigna/fastutil/blob/master/CHANGES) - [Commits](https://github.com/vigna/fastutil/commits/8.5.19) --- updated-dependencies: - dependency-name: it.unimi.dsi:fastutil dependency-version: 8.5.19 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f3c94730..f528f70a 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ it.unimi.dsi fastutil - 8.5.18 + 8.5.19 From 575fe48d6824736953db64bd612f0d3f9f14b527 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Mon, 17 Aug 2026 20:29:48 +0200 Subject: [PATCH 260/271] Harden serialization: fail on unknown tags, fix char encoding (#284) * Harden serialization: fail on unknown tags, fix char encoding Three scoped fixes to the serialization layer. No change to the on-disk format, so no VERSION bump. - deserialize() silently returned null for an unrecognized type tag, surfacing later as a confusing ClassCastException or silent data loss. It now throws IOException naming the tag. The preceding `case -1` was unreachable (readUnsignedByte never returns -1) and is removed. - CHAR and CHAR_ARRAY relied on DataOutput.writeChar/readChar, but DataInputOutput implements those with 4 bytes instead of the 2 the interface specifies. Production writes via DataOutputStream, so no stored data is affected, but graphstore's own tests were round-tripping an encoding that never reaches disk. Both sides now use writeShort/readUnsignedShort, which is byte-identical to DataOutputStream.writeChar, and DataInputOutput.writeChar/readChar are fixed to honour the contract. - Dropped Locale support. Locale is not an AttributeUtils supported type, so it cannot enter a graph through the public API. Tag 124 is kept reserved so it is never reused. Adds a test asserting all serialization tag constants are distinct. Co-Authored-By: Claude Opus 5 (1M context) * Cover non-ASCII chars in serialization tests The char tests only used ASCII values, so they could not detect a narrowing of the 2-byte encoding. Extend them across the boundaries of the 16-bit range: above 0x7F, above 0x7FF, either side of the signed-short flip, the 16-bit maximum, and an unpaired surrogate. Verified by temporarily narrowing CHAR to a symmetric 1-byte encoding, which the previous values did not catch. Co-Authored-By: Claude Opus 5 (1M context) --------- Co-authored-by: Claude Opus 5 (1M context) --- .../org/gephi/graph/impl/Serialization.java | 28 ++++++-------- .../graph/impl/utils/DataInputOutput.java | 4 +- .../gephi/graph/impl/SerializationTest.java | 38 +++++++++++++------ 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 2ae91f03..07d630be 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -56,7 +56,6 @@ import java.time.ZoneId; import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import org.gephi.graph.api.Configuration; @@ -178,6 +177,8 @@ public class Serialization { final static int STRING_EMPTY = 101; final static int NOTUSED_STRING_255 = 102; final static int STRING = 103; + // Reserved, do not reuse: was java.util.Locale, removed because Locale isn't an + // AttributeUtils supported type. Kept so old streams can still be identified. final static int LOCALE = 124; final static int PROPERTIES = 125; final static int CLASS = 126; @@ -1470,7 +1471,9 @@ protected void serialize(final DataOutput out, final Object obj) throws IOExcept } } else if (clazz == Character.class) { out.write(CHAR); - out.writeChar((Character) obj); + // Write as 2-byte short so the encoding doesn't depend on the DataOutput + // implementation. Byte-identical to DataOutputStream.writeChar(). + out.writeShort((Character) obj); } else if (clazz == String.class) { String s = (String) obj; @@ -1520,7 +1523,8 @@ protected void serialize(final DataOutput out, final Object obj) throws IOExcept char[] a = (char[]) obj; LongPacker.packInt(out, a.length); for (char s : a) { - out.writeChar(s); + // See CHAR above: 2-byte encoding, independent of the DataOutput impl. + out.writeShort(s); } } else if (obj instanceof byte[]) { byte[] b = (byte[]) obj; @@ -1531,12 +1535,6 @@ protected void serialize(final DataOutput out, final Object obj) throws IOExcept out.write(DATE); out.writeLong(((Date) obj).getTime()); - } else if (clazz == Locale.class) { - out.write(LOCALE); - Locale l = (Locale) obj; - out.writeUTF(l.getLanguage()); - out.writeUTF(l.getCountry()); - out.writeUTF(l.getVariant()); } else if (obj instanceof String[]) { String[] b = (String[]) obj; out.write(STRING_ARRAY); @@ -2023,11 +2021,11 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce size = LongPacker.unpackInt(is); ret = new char[size]; for (int i = 0; i < size; i++) { - ((char[]) ret)[i] = is.readChar(); + ((char[]) ret)[i] = (char) is.readUnsignedShort(); } break; case CHAR: - ret = is.readChar(); + ret = Character.valueOf((char) is.readUnsignedShort()); break; case FLOAT_MINUS_1: ret = Float.valueOf(-1); @@ -2116,9 +2114,6 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce case ARRAY_BYTE_INT: ret = deserializeArrayByteInt(is); break; - case LOCALE: - ret = new Locale(is.readUTF(), is.readUTF(), is.readUTF()); - break; case STRING_ARRAY: ret = deserializeStringArray(is); break; @@ -2224,9 +2219,8 @@ protected Object deserialize(DataInput is) throws IOException, ClassNotFoundExce case INSTANT: ret = deserializeInstant(is); break; - case -1: - throw new EOFException(); - + default: + throw new IOException("Unknown serialization type tag: " + head); } return ret; } diff --git a/src/main/java/org/gephi/graph/impl/utils/DataInputOutput.java b/src/main/java/org/gephi/graph/impl/utils/DataInputOutput.java index e782fde6..a54bf111 100644 --- a/src/main/java/org/gephi/graph/impl/utils/DataInputOutput.java +++ b/src/main/java/org/gephi/graph/impl/utils/DataInputOutput.java @@ -124,7 +124,7 @@ public int readUnsignedShort() throws IOException { @Override public char readChar() throws IOException { - return (char) readInt(); + return (char) readUnsignedShort(); } @Override @@ -209,7 +209,7 @@ public void writeShort(int v) throws IOException { @Override public void writeChar(int v) throws IOException { - writeInt(v); + writeShort(v); } @Override diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 028e8050..92d2682f 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -42,6 +42,8 @@ import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; import java.io.DataOutput; import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; import java.time.Instant; @@ -53,7 +55,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.Set; @@ -999,7 +1000,10 @@ public void testFloat() throws IOException, ClassNotFoundException { @Test public void testChar() throws IOException, ClassNotFoundException { Serialization ser = new Serialization(null); - char[] vals = { 'a', ' ' }; + // Chars are written as a 2-byte short, so cover the boundaries of that + // range: above 0x7F, above 0x7FF, either side of the signed-short flip, + // the 16-bit maximum, and an unpaired surrogate + char[] vals = { 'a', ' ', '\u00e9', '\u4e2d', '\u7fff', '\u8000', '\uffff', '\ud83d' }; for (char i : vals) { byte[] buf = ser.serialize(i); Object l2 = ser.deserialize(buf); @@ -1144,7 +1148,7 @@ public void testByteArray() throws ClassNotFoundException, IOException { @Test public void testCharArray() throws ClassNotFoundException, IOException { Serialization ser = new Serialization(null); - char[] l = new char[] { '1', 'a', '&' }; + char[] l = new char[] { '1', 'a', '&', '\u00e9', '\u4e2d', '\u8000', '\uffff' }; Object deserialize = ser.deserialize(ser.serialize(l)); Assert.assertTrue(Arrays.equals(l, (char[]) deserialize)); } @@ -1184,14 +1188,6 @@ public void testBigInteger() throws IOException, ClassNotFoundException { Assert.assertEquals(d, ser.deserialize(ser.serialize(d))); } - @Test - public void testLocale() throws Exception { - Serialization ser = new Serialization(null); - Assert.assertEquals(Locale.FRANCE, ser.deserialize(ser.serialize(Locale.FRANCE))); - Assert.assertEquals(Locale.CANADA_FRENCH, ser.deserialize(ser.serialize(Locale.CANADA_FRENCH))); - Assert.assertEquals(Locale.SIMPLIFIED_CHINESE, ser.deserialize(ser.serialize(Locale.SIMPLIFIED_CHINESE))); - } - @Test public void testSmallGraphModel() throws Exception { GraphModelImpl gm = GraphGenerator.generateSmallGraphStore().graphModel; @@ -1314,4 +1310,24 @@ public void testBitVectorEqual() throws Exception { Assert.assertEquals(bs, deserializedBs); } } + + @Test + public void testSerializationTagsAreUnique() throws Exception { + // NULL_ID is excluded: it's an idMap sentinel (-1), not a wire tag + Map tagsByValue = new HashMap<>(); + for (Field field : Serialization.class.getDeclaredFields()) { + int modifiers = field.getModifiers(); + if (!Modifier.isStatic(modifiers) || !Modifier.isFinal(modifiers) || field.getType() != int.class) { + continue; + } + String name = field.getName(); + if (name.equals("NULL_ID")) { + continue; + } + field.setAccessible(true); + int value = field.getInt(null); + String previous = tagsByValue.put(value, name); + Assert.assertNull(previous, "Duplicate serialization tag " + value + " shared by " + previous + " and " + name); + } + } } From 12a2d110d77c2f8ccb697b0c856fbf14358bb3c2 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 18 Aug 2026 20:49:14 +0200 Subject: [PATCH 261/271] Make graph attributes iteration order canonical (#286) GraphAttributesImpl backed its map with a java.util.HashMap, and Serialization.serializeGraphAttributes iterates that map's entrySet() straight into the byte stream. HashMap iteration order is an implementation detail, so the serialized bytes were a function of insertion history rather than of content alone. Switch the field to a TreeMap so iteration is sorted by key and the output bytes become a pure function of the content. This is a prerequisite for byte-pinned serialization fixtures. TreeMap rejects null keys where HashMap accepted them, and throws NPE from deep inside the map on get(null)/containsKey(null) where HashMap returned null/false. Add explicit Objects.requireNonNull(key, "key") guards to every public method taking a key so the failure is intentional and well-messaged. deepHashCode and deepEquals are unchanged and remain correct: Map.hashCode() is specified as the order-independent sum of entry hash codes, and deepEquals goes through MapDeepEquals which compares by key lookup. The read path is unaffected -- deserializeGraphAttributes just puts entries into the map -- so previously written files still load identically. Only the order of newly written graph-attribute entries changes; the format itself is untouched and Serialization.VERSION is not bumped. Co-authored-by: Claude Opus 5 (1M context) --- .../gephi/graph/impl/GraphAttributesImpl.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java b/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java index 3ffc08bd..a23fa322 100644 --- a/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphAttributesImpl.java @@ -15,9 +15,10 @@ */ package org.gephi.graph.impl; -import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.TreeMap; import org.gephi.graph.api.AttributeUtils; import org.gephi.graph.api.Interval; import org.gephi.graph.api.types.TimeMap; @@ -25,13 +26,17 @@ public class GraphAttributesImpl { - protected final Map attributes = new HashMap<>(); + // A TreeMap is used so the iteration order is canonical (sorted by key). + // This makes serialization a pure function of the content rather than of + // the insertion history, which is required for byte-pinned fixtures. + protected final Map attributes = new TreeMap<>(); public synchronized Set getKeys() { return attributes.keySet(); } public synchronized void setValue(String key, Object value) { + Objects.requireNonNull(key, "key"); if (value != null) { checkSupportedTypes(value.getClass()); } @@ -39,18 +44,22 @@ public synchronized void setValue(String key, Object value) { } public synchronized void removeValue(String key) { + Objects.requireNonNull(key, "key"); attributes.remove(key); } public synchronized Object getValue(String key) { + Objects.requireNonNull(key, "key"); return attributes.get(key); } public synchronized Object getValue(String key, double timestamp) { + Objects.requireNonNull(key, "key"); return getValueInternal(key, timestamp); } public synchronized Object getValue(String key, Interval interval) { + Objects.requireNonNull(key, "key"); return getValueInternal(key, interval); } @@ -63,11 +72,13 @@ private Object getValueInternal(String key, Object timeObj) { } public synchronized void removeValue(String key, double timestamp) { + Objects.requireNonNull(key, "key"); removeValueInternal(key, timestamp); } public synchronized void removeValue(String key, Interval interval) { + Objects.requireNonNull(key, "key"); removeValueInternal(key, interval); } @@ -83,10 +94,12 @@ private void removeValueInternal(String key, Object timeObj) { } public synchronized void setValue(String key, Object value, double timestamp) { + Objects.requireNonNull(key, "key"); setValueInternal(key, value, timestamp); } public synchronized void setValue(String key, Object value, Interval interval) { + Objects.requireNonNull(key, "key"); setValueInternal(key, value, interval); } From ca4c6f112884d697a8989775a0983407c278c452 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 19 Aug 2026 17:41:20 +0200 Subject: [PATCH 262/271] Serialization golden fixtures (#287) * Add golden-fixture regression suite for the serialization format Co-authored-by: Claude Opus 5 (1M context) --- .../impl/SerializationCompatibilityTest.java | 613 ++++++++++++++++++ .../impl/SerializationFixtureGenerator.java | 599 +++++++++++++++++ .../serialization/0.4/graph-basic.graphstore | Bin 0 -> 831 bytes .../0.4/graph-parallel.graphstore | Bin 0 -> 887 bytes .../serialization/0.5/graph-basic.graphstore | Bin 0 -> 832 bytes .../0.5/graph-parallel.graphstore | Bin 0 -> 888 bytes .../serialization/0.6/graph-basic.graphstore | Bin 0 -> 832 bytes .../0.6/graph-parallel.graphstore | Bin 0 -> 888 bytes .../serialization/0.7/graph-basic.graphstore | Bin 0 -> 832 bytes .../0.7/graph-parallel.graphstore | Bin 0 -> 888 bytes .../serialization/0.8/graph-basic.graphstore | Bin 0 -> 832 bytes .../0.8/graph-parallel.graphstore | Bin 0 -> 888 bytes .../0.8/graph-types-interval.graphstore | Bin 0 -> 7265 bytes .../0.8/graph-types-timestamp.graphstore | Bin 0 -> 7077 bytes .../serialization/0.8/graph-views.graphstore | Bin 0 -> 1408 bytes src/test/resources/serialization/README.md | 51 ++ 16 files changed, 1263 insertions(+) create mode 100644 src/test/java/org/gephi/graph/impl/SerializationCompatibilityTest.java create mode 100644 src/test/java/org/gephi/graph/impl/SerializationFixtureGenerator.java create mode 100644 src/test/resources/serialization/0.4/graph-basic.graphstore create mode 100644 src/test/resources/serialization/0.4/graph-parallel.graphstore create mode 100644 src/test/resources/serialization/0.5/graph-basic.graphstore create mode 100644 src/test/resources/serialization/0.5/graph-parallel.graphstore create mode 100644 src/test/resources/serialization/0.6/graph-basic.graphstore create mode 100644 src/test/resources/serialization/0.6/graph-parallel.graphstore create mode 100644 src/test/resources/serialization/0.7/graph-basic.graphstore create mode 100644 src/test/resources/serialization/0.7/graph-parallel.graphstore create mode 100644 src/test/resources/serialization/0.8/graph-basic.graphstore create mode 100644 src/test/resources/serialization/0.8/graph-parallel.graphstore create mode 100644 src/test/resources/serialization/0.8/graph-types-interval.graphstore create mode 100644 src/test/resources/serialization/0.8/graph-types-timestamp.graphstore create mode 100644 src/test/resources/serialization/0.8/graph-views.graphstore create mode 100644 src/test/resources/serialization/README.md diff --git a/src/test/java/org/gephi/graph/impl/SerializationCompatibilityTest.java b/src/test/java/org/gephi/graph/impl/SerializationCompatibilityTest.java new file mode 100644 index 00000000..911782ea --- /dev/null +++ b/src/test/java/org/gephi/graph/impl/SerializationCompatibilityTest.java @@ -0,0 +1,613 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Instant; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.TimeFormat; +import org.gephi.graph.api.TimeRepresentation; +import org.gephi.graph.api.types.IntervalBooleanMap; +import org.gephi.graph.api.types.IntervalCharMap; +import org.gephi.graph.api.types.IntervalDoubleMap; +import org.gephi.graph.api.types.IntervalIntegerMap; +import org.gephi.graph.api.types.IntervalSet; +import org.gephi.graph.api.types.IntervalStringMap; +import org.gephi.graph.api.types.TimestampBooleanMap; +import org.gephi.graph.api.types.TimestampCharMap; +import org.gephi.graph.api.types.TimestampDoubleMap; +import org.gephi.graph.api.types.TimestampIntegerMap; +import org.gephi.graph.api.types.TimestampSet; +import org.gephi.graph.api.types.TimestampStringMap; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Golden-fixture regression suite for the serialization format. + *

+ * Enforces three contracts: + *

    + *
  1. Backward compatibility - every fixture, from the oldest minor to the current one, deserializes and yields + * the expected content.
  2. + *
  3. Format drift - for the current minor, serializing the model built by {@link SerializationFixtureGenerator} + * produces bytes identical to the committed file. Older minors are exempt, since graphstore no longer writes those + * formats.
  4. + *
  5. Determinism - the same content serializes to identical bytes regardless of build order.
  6. + *
+ * + * See src/test/resources/serialization/README.md before changing any fixture file. + */ +public class SerializationCompatibilityTest { + + private static final String RESOURCE_ROOT = "/serialization"; + private static final String CURRENT_MINOR = SerializationFixtureGenerator.CURRENT_MINOR; + private static final String[] ALL_MINORS = { "0.4", "0.5", "0.6", "0.7", CURRENT_MINOR }; + + // Column counts of a default model, as asserted on the legacy fixtures + private static final int DEFAULT_NODE_COLUMNS = 3; + private static final int DEFAULT_EDGE_COLUMNS = 4; + + // Columns added by SerializationFixtureGenerator on the type-surface fixtures + private static final int GENERATED_STATIC_COLUMNS = 25; + private static final int GENERATED_DYNAMIC_COLUMNS = 10; + private static final int GENERATED_COLLECTION_COLUMNS = 3; + + // Contract 1: every minor deserializes and holds the expected content + + @Test(dataProvider = "allFixtures") + public void testFixtureDeserializes(String minor, String fixture) throws IOException { + byte[] committed = readFixture(minor, fixture); + Assert.assertTrue(committed.length > 0, "Fixture " + path(minor, fixture) + " is empty"); + + GraphModel graphModel = deserialize(committed); + Assert.assertNotNull(graphModel, "Deserialization of " + path(minor, fixture) + " returned null"); + assertContent(minor, fixture, graphModel); + } + + // Contract 2: the current minor is byte-pinned + // + // Compares today's write path against the committed golden file. Reading a fixture back and re-serializing it + // would not work, because deserialization is not byte-idempotent here: GraphVersion counters and + // TimeIndexStore.countMap are restored from the stream and then incremented again as elements are re-inserted, and + // TextProperties width/height are dropped on read. Contract 1 covers the read path. + + @Test(dataProvider = "currentMinorFixtures") + public void testCurrentMinorIsByteIdentical(String fixture) throws IOException { + byte[] committed = readFixture(CURRENT_MINOR, fixture); + byte[] regenerated = SerializationFixtureGenerator.serialize(buildFixtureModel(fixture)); + + assertBytesEqual(committed, regenerated, "Serialization format drift for fixture " + path(CURRENT_MINOR, fixture) + ".\nThe model built by SerializationFixtureGenerator no longer serializes to the committed bytes.\nThis is either a bug or an intended format change; see src/test/resources/serialization/README.md."); + } + + // Contract 3: determinism + + @Test(dataProvider = "currentMinorFixtures") + public void testSerializationIsDeterministic(String fixture) throws IOException { + GraphModel graphModel = deserialize(readFixture(CURRENT_MINOR, fixture)); + + byte[] first = SerializationFixtureGenerator.serialize(graphModel); + byte[] second = SerializationFixtureGenerator.serialize(graphModel); + + assertBytesEqual(first, second, "Serializing the same model twice produced different bytes for fixture " + path(CURRENT_MINOR, fixture) + ".\nSomething in the write path depends on iteration order or identity" + " hashing rather than on content."); + } + + @Test(dataProvider = "currentMinorFixtures") + public void testFreshlyBuiltModelsSerializeDeterministically(String fixture) throws IOException { + byte[] first = SerializationFixtureGenerator.serialize(buildFixtureModel(fixture)); + byte[] second = SerializationFixtureGenerator.serialize(buildFixtureModel(fixture)); + + assertBytesEqual(first, second, "Building the model for fixture '" + fixture + "' twice and serializing produced different bytes."); + } + + @Test + public void testGraphAttributesOrderIsCanonical() throws IOException { + // The same graph attributes inserted in opposite orders must serialize identically. See GraphAttributesImpl. + String[] keys = { "zulu", "alpha", "mike", "bravo", "yankee", "charlie", "november", "delta" }; + + GraphModel forward = GraphModel.Factory.newInstance(); + for (int i = 0; i < keys.length; i++) { + forward.getGraph().setAttribute(keys[i], "value-" + i); + } + + GraphModel backward = GraphModel.Factory.newInstance(); + for (int i = keys.length - 1; i >= 0; i--) { + backward.getGraph().setAttribute(keys[i], "value-" + i); + } + + assertBytesEqual(SerializationFixtureGenerator.serialize(forward), SerializationFixtureGenerator + .serialize(backward), "Graph attributes are not serialized in a canonical order: the same attributes" + " inserted in a different order produced different bytes."); + } + + // Round-trip only, no byte assertions: the hash-ordered surface excluded from the fixtures + + @Test + public void testRoundTripHashOrderedAttributes() throws IOException { + GraphModel graphModel = GraphModel.Factory.newInstance(); + graphModel.getNodeTable().addColumn("c_list", List.class); + graphModel.getNodeTable().addColumn("c_set", Set.class); + graphModel.getNodeTable().addColumn("c_map", Map.class); + + Graph graph = graphModel.getGraph(); + Node node = graphModel.factory().newNode("n1"); + graph.addNode(node); + + List list = new ArrayList<>(Arrays.asList("a", "b", "c")); + Set set = new HashSet<>(Arrays.asList("x", "y", "z")); + Map map = new HashMap<>(); + map.put("k1", "v1"); + map.put("k2", "v2"); + map.put("k3", "v3"); + + node.setAttribute("c_list", list); + node.setAttribute("c_set", set); + node.setAttribute("c_map", map); + + // Many graph attribute keys, of assorted types + for (int i = 0; i < 50; i++) { + graph.setAttribute("key-" + i, "value-" + i); + } + graph.setAttribute("an-int", 7); + graph.setAttribute("a-char", 'q'); + graph.setAttribute("an-array", new int[] { 1, 2, 3 }); + + GraphModel read = deserialize(SerializationFixtureGenerator.serialize(graphModel)); + Graph readGraph = read.getGraph(); + Node readNode = readGraph.getNode("n1"); + Assert.assertNotNull(readNode); + + Assert.assertEquals(new ArrayList<>((List) readNode.getAttribute("c_list")), list); + Assert.assertEquals(new HashSet<>((Set) readNode.getAttribute("c_set")), set); + Assert.assertEquals(new HashMap<>((Map) readNode.getAttribute("c_map")), map); + + Assert.assertEquals(readGraph.getAttributeKeys().size(), 53); + for (int i = 0; i < 50; i++) { + Assert.assertEquals(readGraph.getAttribute("key-" + i), "value-" + i); + } + Assert.assertEquals(readGraph.getAttribute("an-int"), 7); + Assert.assertEquals(readGraph.getAttribute("a-char"), 'q'); + Assert.assertEquals((int[]) readGraph.getAttribute("an-array"), new int[] { 1, 2, 3 }); + } + + // Data providers + + @DataProvider(name = "allFixtures") + public Object[][] allFixtures() { + List rows = new ArrayList<>(); + for (String minor : ALL_MINORS) { + for (String fixture : fixturesFor(minor)) { + rows.add(new Object[] { minor, fixture }); + } + } + return rows.toArray(new Object[0][]); + } + + @DataProvider(name = "currentMinorFixtures") + public Object[][] currentMinorFixtures() { + String[] fixtures = fixturesFor(CURRENT_MINOR); + Object[][] rows = new Object[fixtures.length][]; + for (int i = 0; i < fixtures.length; i++) { + rows[i] = new Object[] { fixtures[i] }; + } + return rows; + } + + private static String[] fixturesFor(String minor) { + if (CURRENT_MINOR.equals(minor)) { + return new String[] { SerializationFixtureGenerator.BASIC, SerializationFixtureGenerator.PARALLEL, SerializationFixtureGenerator.TYPES_TIMESTAMP, SerializationFixtureGenerator.TYPES_INTERVAL, SerializationFixtureGenerator.VIEWS }; + } + // The legacy fixtures only cover the format skeleton + return new String[] { SerializationFixtureGenerator.BASIC, SerializationFixtureGenerator.PARALLEL }; + } + + // Content assertions + + private void assertContent(String minor, String fixture, GraphModel graphModel) { + switch (fixture) { + case SerializationFixtureGenerator.BASIC: + assertBasic(graphModel); + break; + case SerializationFixtureGenerator.PARALLEL: + assertParallel(graphModel); + break; + case SerializationFixtureGenerator.TYPES_TIMESTAMP: + assertTypeSurface(graphModel, TimeRepresentation.TIMESTAMP); + break; + case SerializationFixtureGenerator.TYPES_INTERVAL: + assertTypeSurface(graphModel, TimeRepresentation.INTERVAL); + break; + case SerializationFixtureGenerator.VIEWS: + assertViews(graphModel); + break; + default: + Assert.fail("No content assertions defined for fixture " + path(minor, fixture)); + } + } + + private void assertBasic(GraphModel graphModel) { + Graph graph = graphModel.getGraph(); + Assert.assertEquals(graph.getNodeCount(), 2); + Assert.assertEquals(graph.getEdgeCount(), 1); + Assert.assertEquals(graphModel.getNodeTable().countColumns(), DEFAULT_NODE_COLUMNS); + Assert.assertEquals(graphModel.getEdgeTable().countColumns(), DEFAULT_EDGE_COLUMNS); + + Node node1 = graph.getNode(SerializationFixtureGenerator.NODE_ID_1); + Node node2 = graph.getNode(SerializationFixtureGenerator.NODE_ID_2); + Assert.assertNotNull(node1); + Assert.assertNotNull(node2); + Assert.assertEquals(node1.getLabel(), SerializationFixtureGenerator.NODE_LABEL_1); + Assert.assertEquals(node2.getLabel(), SerializationFixtureGenerator.NODE_LABEL_2); + Assert.assertEquals(node1.x(), 10.0f); + Assert.assertEquals(node1.y(), 10.0f); + Assert.assertEquals(node1.z(), 1.0f); + Assert.assertEquals(node1.size(), 11.0f); + + Edge edge = graph.getEdge(SerializationFixtureGenerator.EDGE_ID_1); + Assert.assertNotNull(edge); + Assert.assertEquals(edge.getSource(), node1); + Assert.assertEquals(edge.getTarget(), node2); + Assert.assertTrue(edge.isDirected()); + Assert.assertEquals(edge.getWeight(), 1.0); + } + + private void assertParallel(GraphModel graphModel) { + Graph graph = graphModel.getGraph(); + Assert.assertEquals(graph.getNodeCount(), 2); + Assert.assertEquals(graph.getEdgeCount(), 2); + Assert.assertEquals(graphModel.getNodeTable().countColumns(), DEFAULT_NODE_COLUMNS); + Assert.assertEquals(graphModel.getEdgeTable().countColumns(), DEFAULT_EDGE_COLUMNS); + + Edge edge1 = graph.getEdge(SerializationFixtureGenerator.EDGE_ID_1); + Edge edge2 = graph.getEdge(SerializationFixtureGenerator.EDGE_ID_2); + Assert.assertNotNull(edge1); + Assert.assertNotNull(edge2); + Assert.assertNotEquals(edge1.getType(), edge2.getType()); + Assert.assertEquals(edge1.getTypeLabel(), SerializationFixtureGenerator.EDGE_TYPE_1); + Assert.assertEquals(edge2.getTypeLabel(), SerializationFixtureGenerator.EDGE_TYPE_2); + } + + private void assertTypeSurface(GraphModel graphModel, TimeRepresentation timeRepresentation) { + boolean interval = timeRepresentation == TimeRepresentation.INTERVAL; + + Configuration config = graphModel.getConfiguration(); + Assert.assertEquals(config.getTimeRepresentation(), timeRepresentation); + Assert.assertEquals(config.getEdgeWeightType(), interval ? IntervalDoubleMap.class : TimestampDoubleMap.class); + Assert.assertEquals(graphModel.getTimeFormat(), interval ? TimeFormat.DATETIME : TimeFormat.DOUBLE); + Assert.assertEquals(graphModel.getTimeZone(), ZoneId.of("Europe/Paris")); + + Assert.assertEquals(graphModel.getNodeTable() + .countColumns(), DEFAULT_NODE_COLUMNS + GENERATED_STATIC_COLUMNS + GENERATED_DYNAMIC_COLUMNS + GENERATED_COLLECTION_COLUMNS); + Assert.assertEquals(graphModel.getEdgeTable() + .countColumns(), DEFAULT_EDGE_COLUMNS + GENERATED_STATIC_COLUMNS + GENERATED_DYNAMIC_COLUMNS); + + // Column types survived the round trip, including the boxed-array standardization + Assert.assertEquals(graphModel.getNodeTable().getColumn("t_character").getTypeClass(), Character.class); + Assert.assertEquals(graphModel.getNodeTable().getColumn("t_char_array").getTypeClass(), char[].class); + Assert.assertEquals(graphModel.getNodeTable().getColumn("t_boxed_boolean_array") + .getTypeClass(), boolean[].class); + Assert.assertEquals(graphModel.getNodeTable().getColumn("t_boxed_character_array") + .getTypeClass(), char[].class); + Assert.assertEquals(graphModel.getNodeTable().getColumn("t_set").getTypeClass(), Set.class); + Assert.assertEquals(graphModel.getNodeTable().getColumn("t_map").getTypeClass(), Map.class); + + Graph graph = graphModel.getGraph(); + Assert.assertEquals(graph.getNodeCount(), 2); + Assert.assertEquals(graph.getEdgeCount(), 1); + + Node node1 = graph.getNode(SerializationFixtureGenerator.NODE_ID_1); + Assert.assertNotNull(node1); + assertStaticValues(node1); + assertDynamicValues(node1, timeRepresentation); + + // Collections: only the list carries a value, see SerializationFixtureGenerator + Assert.assertEquals(new ArrayList<>((List) node1.getAttribute("t_list")), Arrays.asList("first", "second")); + Assert.assertNull(node1.getAttribute("t_set")); + Assert.assertNull(node1.getAttribute("t_map")); + + // Element and text properties + Assert.assertEquals(node1.x(), 1.5f); + Assert.assertEquals(node1.y(), -2.5f); + Assert.assertEquals(node1.z(), 3.5f); + Assert.assertEquals(node1.size(), 7.25f); + Assert.assertEquals(node1.alpha(), 0.75f, 0.005f); + Assert.assertEquals(node1.getTextProperties().getText(), "node one text"); + Assert.assertEquals(node1.getTextProperties().getSize(), 13.5f); + Assert.assertFalse(node1.getTextProperties().isVisible()); + // The fixture was written with text dimensions 42x24 and the bytes carry them, but NodeImpl.setTextProperties + // does not copy width and height back, so they are lost on read. Pinned here as the current behaviour. + Assert.assertEquals(node1.getTextProperties().getWidth(), 0.0f); + Assert.assertEquals(node1.getTextProperties().getHeight(), 0.0f); + + // Element time set + Node node2 = graph.getNode(SerializationFixtureGenerator.NODE_ID_2); + Assert.assertNotNull(node2); + if (interval) { + Assert.assertEquals(node1.getIntervals(), new Interval[] { new Interval(1.0, 5.0) }); + Assert.assertEquals(node2.getIntervals(), new Interval[] { new Interval(2.0, 3.0) }); + } else { + Assert.assertEquals(node1.getTimestamps(), new double[] { 1.0, 4.0 }); + Assert.assertEquals(node2.getTimestamps(), new double[] { 2.0 }); + } + + // Edge, edge type and dynamic weight + Edge edge1 = graph.getEdge(SerializationFixtureGenerator.EDGE_ID_1); + Assert.assertNotNull(edge1); + Assert.assertEquals(edge1.getTypeLabel(), SerializationFixtureGenerator.EDGE_TYPE_1); + Assert.assertTrue(edge1.hasDynamicWeight()); + Assert.assertEquals(edge1.getTextProperties().getText(), "edge one text"); + assertStaticValues(edge1); + assertDynamicValues(edge1, timeRepresentation); + if (interval) { + Assert.assertEquals(edge1.getWeight(new Interval(1.0, 2.0)), 2.5); + Assert.assertEquals(edge1.getWeight(new Interval(3.0, 4.0)), 3.5); + } else { + Assert.assertEquals(edge1.getWeight(1.0), 2.5); + Assert.assertEquals(edge1.getWeight(3.0), 3.5); + } + + // Graph attributes + Assert.assertEquals(graph.getAttribute("attr-string"), "graph level"); + Assert.assertEquals(graph.getAttribute("attr-int"), 42); + Assert.assertEquals(graph.getAttribute("attr-double"), 3.5); + Assert.assertEquals(graph.getAttribute("attr-char"), 'g'); + Assert.assertEquals((char[]) graph.getAttribute("attr-char-array"), new char[] { 'a', 'b' }); + Assert.assertEquals(graph.getAttribute("attr-instant"), Instant.ofEpochSecond(1_600_000_000L, 123)); + if (interval) { + Assert.assertEquals(graph.getAttribute("attr-dynamic", new Interval(1.0, 2.0)), "first"); + Assert.assertEquals(graph.getAttribute("attr-dynamic", new Interval(3.0, 4.0)), "second"); + } else { + Assert.assertEquals(graph.getAttribute("attr-dynamic", 1.0), "first"); + Assert.assertEquals(graph.getAttribute("attr-dynamic", 3.0), "second"); + } + } + + private void assertStaticValues(org.gephi.graph.api.Element element) { + Assert.assertEquals(element.getAttribute("t_boolean"), Boolean.TRUE); + Assert.assertEquals(element.getAttribute("t_integer"), 123456); + Assert.assertEquals(element.getAttribute("t_short"), (short) -12); + Assert.assertEquals(element.getAttribute("t_long"), 9876543210L); + Assert.assertEquals(element.getAttribute("t_biginteger"), new BigInteger("123456789012345678901234567890")); + Assert.assertEquals(element.getAttribute("t_byte"), (byte) 7); + Assert.assertEquals(element.getAttribute("t_float"), 1.25f); + Assert.assertEquals(element.getAttribute("t_double"), -2.5); + Assert.assertEquals(element.getAttribute("t_bigdecimal"), new BigDecimal("1234567890.0987654321")); + Assert.assertEquals(element.getAttribute("t_character"), 'Z'); + Assert.assertEquals(element.getAttribute("t_string"), "hello é中文"); + Assert.assertEquals(element.getAttribute("t_instant"), Instant.ofEpochSecond(1_500_000_000L, 42)); + + Assert.assertEquals((boolean[]) element.getAttribute("t_boolean_array"), new boolean[] { true, false, true }); + Assert.assertEquals((int[]) element.getAttribute("t_int_array"), new int[] { 1, -2, 3 }); + Assert.assertEquals((short[]) element.getAttribute("t_short_array"), new short[] { 4, -5 }); + Assert.assertEquals((long[]) element.getAttribute("t_long_array"), new long[] { 6L, -7L }); + // BigInteger[] and BigDecimal[] go through the generic ARRAY_OBJECT path and come back as Object[]. The + // element values survive, the array component type does not. + Assert.assertEquals((Object[]) element + .getAttribute("t_biginteger_array"), new Object[] { BigInteger.ONE, new BigInteger("-99") }); + Assert.assertEquals((byte[]) element.getAttribute("t_byte_array"), new byte[] { 8, -9 }); + Assert.assertEquals((float[]) element.getAttribute("t_float_array"), new float[] { 1.5f, -2.5f }); + Assert.assertEquals((double[]) element.getAttribute("t_double_array"), new double[] { 3.5, -4.5 }); + Assert.assertEquals((Object[]) element + .getAttribute("t_bigdecimal_array"), new Object[] { BigDecimal.ONE, new BigDecimal("-0.5") }); + Assert.assertEquals((char[]) element.getAttribute("t_char_array"), new char[] { 'a', 'é', '中' }); + Assert.assertEquals((String[]) element.getAttribute("t_string_array"), new String[] { "one", "two", "three" }); + + Assert.assertEquals((boolean[]) element.getAttribute("t_boxed_boolean_array"), new boolean[] { false, true }); + Assert.assertEquals((char[]) element.getAttribute("t_boxed_character_array"), new char[] { 'x', 'y' }); + } + + private void assertDynamicValues(org.gephi.graph.api.Element element, TimeRepresentation timeRepresentation) { + if (timeRepresentation == TimeRepresentation.INTERVAL) { + IntervalSet set = (IntervalSet) element.getAttribute("t_interval_set"); + Assert.assertNotNull(set); + Assert.assertEquals(set.size(), 2); + Assert.assertTrue(set.contains(new Interval(1.0, 2.0))); + Assert.assertTrue(set.contains(new Interval(3.0, 4.0))); + + Assert.assertEquals(((IntervalBooleanMap) element.getAttribute("t_interval_boolean")) + .getBoolean(new Interval(1.0, 2.0)), true); + Assert.assertEquals(((IntervalIntegerMap) element.getAttribute("t_interval_integer")) + .getInteger(new Interval(3.0, 4.0)), -20); + Assert.assertEquals(((IntervalCharMap) element.getAttribute("t_interval_char")) + .getCharacter(new Interval(3.0, 4.0)), '中'); + Assert.assertEquals(((IntervalStringMap) element.getAttribute("t_interval_string")) + .get(new Interval(1.0, 2.0), (String) null), "alpha"); + Assert.assertNotNull(element.getAttribute("t_interval_short")); + Assert.assertNotNull(element.getAttribute("t_interval_long")); + Assert.assertNotNull(element.getAttribute("t_interval_byte")); + Assert.assertNotNull(element.getAttribute("t_interval_float")); + Assert.assertNotNull(element.getAttribute("t_interval_double")); + } else { + TimestampSet set = (TimestampSet) element.getAttribute("t_timestamp_set"); + Assert.assertNotNull(set); + Assert.assertEquals(set.toPrimitiveArray(), new double[] { 1.0, 3.0 }); + + Assert.assertEquals(((TimestampBooleanMap) element.getAttribute("t_timestamp_boolean")) + .getBoolean(1.0), true); + Assert.assertEquals(((TimestampIntegerMap) element.getAttribute("t_timestamp_integer")) + .getInteger(3.0), -20); + Assert.assertEquals(((TimestampCharMap) element.getAttribute("t_timestamp_char")).getCharacter(3.0), '中'); + Assert.assertEquals(((TimestampStringMap) element.getAttribute("t_timestamp_string")) + .get(1.0, (String) null), "alpha"); + Assert.assertNotNull(element.getAttribute("t_timestamp_short")); + Assert.assertNotNull(element.getAttribute("t_timestamp_long")); + Assert.assertNotNull(element.getAttribute("t_timestamp_byte")); + Assert.assertNotNull(element.getAttribute("t_timestamp_float")); + Assert.assertNotNull(element.getAttribute("t_timestamp_double")); + } + } + + private void assertViews(GraphModel graphModel) { + Graph graph = graphModel.getGraph(); + Assert.assertEquals(graph.getNodeCount(), 5); + Assert.assertEquals(graph.getEdgeCount(), 5); + + // Default type plus the two registered labels + Assert.assertEquals(graphModel.getEdgeTypeCount(), 3); + Assert.assertTrue(graphModel.isMultiGraph()); + Assert.assertTrue(graphModel.isMixed()); + + Assert.assertTrue(graph.getEdge("e0").isDirected()); + Assert.assertFalse(graph.getEdge("e2").isDirected()); + Assert.assertTrue(graph.getEdge("e4").isSelfLoop()); + Assert.assertEquals(graph.getEdge("e3").getWeight(), 4.0); + + Assert.assertEquals(((GraphModelImpl) graphModel).store.viewStore.length, 2); + + GraphViewImpl nodeAndEdgeView = null; + GraphViewImpl nodeOnlyView = null; + for (GraphViewImpl view : ((GraphModelImpl) graphModel).store.viewStore.views) { + if (view == null) { + continue; + } + if (view.isEdgeView()) { + nodeAndEdgeView = view; + } else { + nodeOnlyView = view; + } + } + + Assert.assertNotNull(nodeAndEdgeView, "The node and edge view is missing"); + Assert.assertTrue(nodeAndEdgeView.isNodeView()); + Graph subGraph = graphModel.getGraph(nodeAndEdgeView); + Assert.assertEquals(subGraph.getNodeCount(), 3); + Assert.assertEquals(subGraph.getEdgeCount(), 1); + Assert.assertEquals(subGraph.getAttribute("view-attr"), "on the view"); + Assert.assertEquals(subGraph.getAttribute("view-count"), 3); + + // The other view is node-only and holds the two remaining nodes + Assert.assertNotNull(nodeOnlyView, "The node-only view is missing"); + Assert.assertEquals(graphModel.getGraph(nodeOnlyView).getNodeCount(), 2); + + // GraphViewStore.visibleView is not part of the serialized format, so it always comes back as the main view + Assert.assertTrue(graphModel.getVisibleView().isMainView()); + + Assert.assertEquals(graph.getAttribute("graph-attr"), "main graph"); + Assert.assertEquals(graph.getNode("n2").getAttribute("weight"), 2.0); + Assert.assertEquals(graph.getNode("n2").getAttribute("tag"), "tag2"); + } + + // Helpers + + private static String path(String minor, String fixture) { + return RESOURCE_ROOT + "/" + minor + "/" + fixture + SerializationFixtureGenerator.FILE_EXTENSION; + } + + private static byte[] readFixture(String minor, String fixture) throws IOException { + String resource = path(minor, fixture); + try (InputStream is = SerializationCompatibilityTest.class.getResourceAsStream(resource)) { + Assert.assertNotNull(is, "Missing fixture resource " + resource); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int read; + while ((read = is.read(buffer)) != -1) { + baos.write(buffer, 0, read); + } + return baos.toByteArray(); + } + } + + private static GraphModel buildFixtureModel(String fixture) { + GraphModel model = SerializationFixtureGenerator.buildAll().get(fixture); + Assert.assertNotNull(model, "SerializationFixtureGenerator does not build fixture '" + fixture + "'"); + return model; + } + + private static GraphModel deserialize(byte[] bytes) throws IOException { + try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes))) { + return GraphModel.Serialization.read(dis); + } + } + + /** + * Byte comparison that reports where the two streams diverge, not just that they do. + * + * @param expected expected bytes + * @param actual actual bytes + * @param context message explaining what the mismatch means + */ + private static void assertBytesEqual(byte[] expected, byte[] actual, String context) { + if (Arrays.equals(expected, actual)) { + return; + } + + int common = Math.min(expected.length, actual.length); + int offset = -1; + for (int i = 0; i < common; i++) { + if (expected[i] != actual[i]) { + offset = i; + break; + } + } + + StringBuilder sb = new StringBuilder(context); + sb.append("\n\nExpected ").append(expected.length).append(" bytes, got ").append(actual.length) + .append(" bytes."); + if (offset < 0) { + offset = common; + sb.append("\nThe first ").append(common) + .append(" bytes are identical; the streams differ in length only, starting at offset ") + .append(common).append('.'); + } else { + sb.append("\nFirst difference at offset ").append(offset).append(" (0x").append(Integer.toHexString(offset)) + .append("): expected 0x").append(hexByte(expected[offset])).append(", got 0x") + .append(hexByte(actual[offset])).append('.'); + } + + int from = Math.max(0, offset - 16); + int to = offset + 16; + sb.append("\n expected[").append(from).append("..").append(Math.min(to, expected.length) - 1).append("]: ") + .append(hexDump(expected, from, to)); + sb.append("\n actual [").append(from).append("..").append(Math.min(to, actual.length) - 1).append("]: ") + .append(hexDump(actual, from, to)); + + Assert.fail(sb.toString()); + } + + private static String hexByte(byte b) { + return String.format("%02x", b); + } + + private static String hexDump(byte[] bytes, int from, int to) { + StringBuilder sb = new StringBuilder(); + for (int i = from; i < Math.min(to, bytes.length); i++) { + if (sb.length() > 0) { + sb.append(' '); + } + sb.append(hexByte(bytes[i])); + } + return sb.length() == 0 ? "" : sb.toString(); + } +} diff --git a/src/test/java/org/gephi/graph/impl/SerializationFixtureGenerator.java b/src/test/java/org/gephi/graph/impl/SerializationFixtureGenerator.java new file mode 100644 index 00000000..34a6c758 --- /dev/null +++ b/src/test/java/org/gephi/graph/impl/SerializationFixtureGenerator.java @@ -0,0 +1,599 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; + +import java.awt.Color; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Instant; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.gephi.graph.api.AttributeUtils; +import org.gephi.graph.api.Configuration; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.GraphView; +import org.gephi.graph.api.Interval; +import org.gephi.graph.api.Node; +import org.gephi.graph.api.Table; +import org.gephi.graph.api.TimeFormat; +import org.gephi.graph.api.TimeRepresentation; +import org.gephi.graph.api.types.IntervalBooleanMap; +import org.gephi.graph.api.types.IntervalByteMap; +import org.gephi.graph.api.types.IntervalCharMap; +import org.gephi.graph.api.types.IntervalDoubleMap; +import org.gephi.graph.api.types.IntervalFloatMap; +import org.gephi.graph.api.types.IntervalIntegerMap; +import org.gephi.graph.api.types.IntervalLongMap; +import org.gephi.graph.api.types.IntervalSet; +import org.gephi.graph.api.types.IntervalShortMap; +import org.gephi.graph.api.types.IntervalStringMap; +import org.gephi.graph.api.types.TimestampBooleanMap; +import org.gephi.graph.api.types.TimestampByteMap; +import org.gephi.graph.api.types.TimestampCharMap; +import org.gephi.graph.api.types.TimestampDoubleMap; +import org.gephi.graph.api.types.TimestampFloatMap; +import org.gephi.graph.api.types.TimestampIntegerMap; +import org.gephi.graph.api.types.TimestampLongMap; +import org.gephi.graph.api.types.TimestampSet; +import org.gephi.graph.api.types.TimestampShortMap; +import org.gephi.graph.api.types.TimestampStringMap; + +/** + * Builds and regenerates the golden serialization fixtures for the current minor version of graphstore. + *

+ * The fixtures live in src/test/resources/serialization/<MINOR>/ and are consumed by + * {@link SerializationCompatibilityTest}. See src/test/resources/serialization/README.md for the layout + * and the regeneration rules. + *

+ * The current minor's fixtures are byte-pinned, so every model built here must serialize to the same bytes on every run + * and every JVM. Run with: + * + *

+ * mvn -q test-compile
+ * mvn -q dependency:build-classpath -Dmdep.outputFile=target/test-cp.txt
+ * java -cp "target/classes:target/test-classes:$(cat target/test-cp.txt)" \
+ *     org.gephi.graph.impl.SerializationFixtureGenerator
+ * 
+ */ +public final class SerializationFixtureGenerator { + + /** + * The minor version the generated fixtures belong to. Must match the project version's MINOR. + */ + public static final String CURRENT_MINOR = "0.8"; + + /** + * Root of the fixture tree, relative to the project base directory. + */ + public static final String FIXTURE_ROOT = "src/test/resources/serialization"; + + public static final String FILE_EXTENSION = ".graphstore"; + + // Fixture names (file name without extension) + public static final String BASIC = "graph-basic"; + public static final String PARALLEL = "graph-parallel"; + public static final String TYPES_TIMESTAMP = "graph-types-timestamp"; + public static final String TYPES_INTERVAL = "graph-types-interval"; + public static final String VIEWS = "graph-views"; + + // Shared constants, also used by the assertions in SerializationCompatibilityTest + public static final String NODE_ID_1 = "node1"; + public static final String NODE_ID_2 = "node2"; + public static final String NODE_LABEL_1 = "Node 1"; + public static final String NODE_LABEL_2 = "Node 2"; + public static final String EDGE_ID_1 = "edge1"; + public static final String EDGE_ID_2 = "edge2"; + public static final String EDGE_TYPE_1 = "foo"; + public static final String EDGE_TYPE_2 = "bar"; + + private SerializationFixtureGenerator() { + // Utility + } + + /** + * Builds every fixture model for the current minor, keyed by fixture name. Iteration order is stable. + * + * @return ordered map of fixture name to freshly built graph model + */ + public static Map buildAll() { + Map models = new LinkedHashMap<>(); + models.put(BASIC, buildBasic()); + models.put(PARALLEL, buildParallel()); + models.put(TYPES_TIMESTAMP, buildTypeSurface(TimeRepresentation.TIMESTAMP)); + models.put(TYPES_INTERVAL, buildTypeSurface(TimeRepresentation.INTERVAL)); + models.put(VIEWS, buildViews()); + return models; + } + + /** + * Two nodes and one edge, mirroring the recipe used for the legacy 0.4 - 0.7 fixtures so the same content + * assertions apply across every minor. + * + * @return graph model + */ + public static GraphModel buildBasic() { + GraphModel gm = GraphModel.Factory.newInstance(); + Node node1 = gm.factory().newNode(NODE_ID_1); + Node node2 = gm.factory().newNode(NODE_ID_2); + + node1.setLabel(NODE_LABEL_1); + node1.setColor(Color.CYAN); + node1.setX(10.0f); + node1.setY(10.0f); + node1.setZ(1.0f); + node1.setSize(11.0f); + node1.setAlpha(0.5f); + + node2.setLabel(NODE_LABEL_2); + node2.setColor(Color.RED); + + gm.getGraph().addNode(node1); + gm.getGraph().addNode(node2); + + Edge edge = gm.factory().newEdge(EDGE_ID_1, node1, node2, 0, 1.0, true); + gm.getGraph().addEdge(edge); + + return gm; + } + + /** + * Two nodes and two parallel edges of distinct types, mirroring the legacy 0.4 - 0.7 recipe. + * + * @return graph model + */ + public static GraphModel buildParallel() { + GraphModel gm = GraphModel.Factory.newInstance(); + Node node1 = gm.factory().newNode(NODE_ID_1); + Node node2 = gm.factory().newNode(NODE_ID_2); + + node1.setLabel(NODE_LABEL_1); + node1.setColor(Color.CYAN); + node1.setX(10.0f); + node1.setY(10.0f); + node1.setZ(1.0f); + node1.setSize(11.0f); + node1.setAlpha(0.5f); + + node2.setLabel(NODE_LABEL_2); + node2.setColor(Color.RED); + + gm.getGraph().addNode(node1); + gm.getGraph().addNode(node2); + + int type1 = gm.addEdgeType(EDGE_TYPE_1); + int type2 = gm.addEdgeType(EDGE_TYPE_2); + + gm.getGraph().addEdge(gm.factory().newEdge(EDGE_ID_1, node1, node2, type1, 1.0, true)); + gm.getGraph().addEdge(gm.factory().newEdge(EDGE_ID_2, node1, node2, type2, 1.0, true)); + + return gm; + } + + /** + * The type-surface fixture: one node column per supported static type, one per dynamic type of the given time + * representation, dynamic edge weights, graph attributes, text properties and non-default element properties. + *

+ * The two time representations select different index-store implementations, hence one fixture each. + * + * @param timeRepresentation time representation to build for + * @return graph model + */ + public static GraphModel buildTypeSurface(TimeRepresentation timeRepresentation) { + boolean interval = timeRepresentation == TimeRepresentation.INTERVAL; + Configuration config = Configuration.builder().timeRepresentation(timeRepresentation) + .edgeWeightType(interval ? IntervalDoubleMap.class : TimestampDoubleMap.class).build(); + GraphModel gm = GraphModel.Factory.newInstance(config); + gm.setTimeFormat(interval ? TimeFormat.DATETIME : TimeFormat.DOUBLE); + gm.setTimeZone(ZoneId.of("Europe/Paris")); + + Table nodeTable = gm.getNodeTable(); + addStaticColumns(nodeTable); + addDynamicColumns(nodeTable, timeRepresentation); + addCollectionColumns(nodeTable); + + Table edgeTable = gm.getEdgeTable(); + addStaticColumns(edgeTable); + addDynamicColumns(edgeTable, timeRepresentation); + + Graph graph = gm.getGraph(); + Node node1 = gm.factory().newNode(NODE_ID_1); + Node node2 = gm.factory().newNode(NODE_ID_2); + node1.setLabel(NODE_LABEL_1); + node2.setLabel(NODE_LABEL_2); + graph.addNode(node1); + graph.addNode(node2); + + // Non-default element and text properties + node1.setColor(new Color(12, 34, 56)); + node1.setAlpha(0.75f); + node1.setPosition(1.5f, -2.5f, 3.5f); + node1.setSize(7.25f); + node1.setFixed(true); + node1.getTextProperties().setColor(new Color(200, 100, 50)); + node1.getTextProperties().setSize(13.5f); + node1.getTextProperties().setVisible(false); + node1.getTextProperties().setText("node one text"); + node1.getTextProperties().setDimensions(42.0f, 24.0f); + + // Fill every static and dynamic column on node1, leave node2 on defaults + setStaticValues(node1); + setDynamicValues(node1, timeRepresentation); + setCollectionValues(node1); + if (interval) { + node1.addInterval(new Interval(1.0, 5.0)); + node2.addInterval(new Interval(2.0, 3.0)); + } else { + node1.addTimestamp(1.0); + node1.addTimestamp(4.0); + node2.addTimestamp(2.0); + } + + int typeFoo = gm.addEdgeType(EDGE_TYPE_1); + Edge edge1 = gm.factory().newEdge(EDGE_ID_1, node1, node2, typeFoo, 1.0, true); + graph.addEdge(edge1); + edge1.setLabel("Edge 1"); + edge1.setColor(new Color(9, 8, 7)); + edge1.getTextProperties().setText("edge one text"); + edge1.getTextProperties().setSize(3.5f); + setStaticValues(edge1); + setDynamicValues(edge1, timeRepresentation); + + // Dynamic edge weight + if (interval) { + edge1.setWeight(2.5, new Interval(1.0, 2.0)); + edge1.setWeight(3.5, new Interval(3.0, 4.0)); + } else { + edge1.setWeight(2.5, 1.0); + edge1.setWeight(3.5, 3.0); + } + + // Graph attributes. Safe to byte-pin: GraphAttributesImpl keeps a canonical (sorted) order. + graph.setAttribute("attr-string", "graph level"); + graph.setAttribute("attr-int", 42); + graph.setAttribute("attr-double", 3.5); + graph.setAttribute("attr-char", 'g'); + graph.setAttribute("attr-char-array", new char[] { 'a', 'b' }); + graph.setAttribute("attr-instant", Instant.ofEpochSecond(1_600_000_000L, 123)); + if (interval) { + graph.setAttribute("attr-dynamic", "first", new Interval(1.0, 2.0)); + graph.setAttribute("attr-dynamic", "second", new Interval(3.0, 4.0)); + } else { + graph.setAttribute("attr-dynamic", "first", 1.0); + graph.setAttribute("attr-dynamic", "second", 3.0); + } + + return gm; + } + + /** + * Views, edge types, mixed directedness and self-loops. + * + * @return graph model + */ + public static GraphModel buildViews() { + GraphModel gm = GraphModel.Factory.newInstance(); + + Table nodeTable = gm.getNodeTable(); + nodeTable.addColumn("weight", Double.class); + nodeTable.addColumn("tag", String.class); + + Graph graph = gm.getGraph(); + Node[] nodes = new Node[5]; + for (int i = 0; i < nodes.length; i++) { + nodes[i] = gm.factory().newNode("n" + i); + nodes[i].setLabel("Node " + i); + nodes[i].setAttribute("weight", (double) i); + nodes[i].setAttribute("tag", "tag" + i); + graph.addNode(nodes[i]); + } + + int typeFoo = gm.addEdgeType(EDGE_TYPE_1); + int typeBar = gm.addEdgeType(EDGE_TYPE_2); + + // Directed, undirected and self-loop edges over multiple types + graph.addEdge(gm.factory().newEdge("e0", nodes[0], nodes[1], 0, 1.0, true)); + graph.addEdge(gm.factory().newEdge("e1", nodes[1], nodes[2], typeFoo, 2.0, true)); + graph.addEdge(gm.factory().newEdge("e2", nodes[2], nodes[3], typeBar, 3.0, false)); + graph.addEdge(gm.factory().newEdge("e3", nodes[3], nodes[4], typeFoo, 4.0, false)); + graph.addEdge(gm.factory().newEdge("e4", nodes[4], nodes[4], typeBar, 5.0, true)); + + // A node+edge view holding a subset + GraphView nodeAndEdgeView = gm.createView(); + Graph subGraph = gm.getGraph(nodeAndEdgeView); + subGraph.addNode(nodes[0]); + subGraph.addNode(nodes[1]); + subGraph.addNode(nodes[2]); + subGraph.addEdge(graph.getEdge("e0")); + subGraph.setAttribute("view-attr", "on the view"); + subGraph.setAttribute("view-count", 3); + + // A node-only view + GraphView nodeOnlyView = gm.createView(true, false); + Graph nodeOnlyGraph = gm.getGraph(nodeOnlyView); + nodeOnlyGraph.addNode(nodes[3]); + nodeOnlyGraph.addNode(nodes[4]); + + // The visible view stays on the main view. GraphViewStore.visibleView is not part of the serialized format, + // see Serialization.serializeViewStore. + + graph.setAttribute("graph-attr", "main graph"); + + return gm; + } + + // Column and value helpers + + /** + * Every supported static type, in a fixed order. Boxed array types are intentionally included: they standardize to + * their primitive counterparts, which is part of the surface worth pinning. + */ + private static void addStaticColumns(Table table) { + table.addColumn("t_boolean", Boolean.class); + table.addColumn("t_integer", Integer.class); + table.addColumn("t_short", Short.class); + table.addColumn("t_long", Long.class); + table.addColumn("t_biginteger", BigInteger.class); + table.addColumn("t_byte", Byte.class); + table.addColumn("t_float", Float.class); + table.addColumn("t_double", Double.class); + table.addColumn("t_bigdecimal", BigDecimal.class); + table.addColumn("t_character", Character.class); + table.addColumn("t_string", String.class); + table.addColumn("t_instant", Instant.class); + + table.addColumn("t_boolean_array", boolean[].class); + table.addColumn("t_int_array", int[].class); + table.addColumn("t_short_array", short[].class); + table.addColumn("t_long_array", long[].class); + table.addColumn("t_biginteger_array", BigInteger[].class); + table.addColumn("t_byte_array", byte[].class); + table.addColumn("t_float_array", float[].class); + table.addColumn("t_double_array", double[].class); + table.addColumn("t_bigdecimal_array", BigDecimal[].class); + table.addColumn("t_char_array", char[].class); + table.addColumn("t_string_array", String[].class); + + // Boxed arrays, standardized to primitive arrays by the table + table.addColumn("t_boxed_boolean_array", Boolean[].class); + table.addColumn("t_boxed_character_array", Character[].class); + } + + private static void addDynamicColumns(Table table, TimeRepresentation timeRepresentation) { + if (timeRepresentation == TimeRepresentation.INTERVAL) { + table.addColumn("t_interval_set", IntervalSet.class); + table.addColumn("t_interval_boolean", IntervalBooleanMap.class); + table.addColumn("t_interval_integer", IntervalIntegerMap.class); + table.addColumn("t_interval_short", IntervalShortMap.class); + table.addColumn("t_interval_long", IntervalLongMap.class); + table.addColumn("t_interval_byte", IntervalByteMap.class); + table.addColumn("t_interval_float", IntervalFloatMap.class); + table.addColumn("t_interval_double", IntervalDoubleMap.class); + table.addColumn("t_interval_char", IntervalCharMap.class); + table.addColumn("t_interval_string", IntervalStringMap.class); + } else { + table.addColumn("t_timestamp_set", TimestampSet.class); + table.addColumn("t_timestamp_boolean", TimestampBooleanMap.class); + table.addColumn("t_timestamp_integer", TimestampIntegerMap.class); + table.addColumn("t_timestamp_short", TimestampShortMap.class); + table.addColumn("t_timestamp_long", TimestampLongMap.class); + table.addColumn("t_timestamp_byte", TimestampByteMap.class); + table.addColumn("t_timestamp_float", TimestampFloatMap.class); + table.addColumn("t_timestamp_double", TimestampDoubleMap.class); + table.addColumn("t_timestamp_char", TimestampCharMap.class); + table.addColumn("t_timestamp_string", TimestampStringMap.class); + } + } + + /** + * List, Set and Map columns. Only the List gets a value: lists round-trip order-preserving, whereas generic sets + * and maps are serialized in hash order and come back as fastutil implementations, so they cannot be byte-pinned. + */ + private static void addCollectionColumns(Table table) { + table.addColumn("t_list", List.class); + table.addColumn("t_set", java.util.Set.class); + table.addColumn("t_map", Map.class); + } + + private static void setStaticValues(org.gephi.graph.api.Element element) { + element.setAttribute("t_boolean", Boolean.TRUE); + element.setAttribute("t_integer", 123456); + element.setAttribute("t_short", (short) -12); + element.setAttribute("t_long", 9876543210L); + element.setAttribute("t_biginteger", new BigInteger("123456789012345678901234567890")); + element.setAttribute("t_byte", (byte) 7); + element.setAttribute("t_float", 1.25f); + element.setAttribute("t_double", -2.5); + element.setAttribute("t_bigdecimal", new BigDecimal("1234567890.0987654321")); + element.setAttribute("t_character", 'Z'); + element.setAttribute("t_string", "hello é中文"); + element.setAttribute("t_instant", Instant.ofEpochSecond(1_500_000_000L, 42)); + + element.setAttribute("t_boolean_array", new boolean[] { true, false, true }); + element.setAttribute("t_int_array", new int[] { 1, -2, 3 }); + element.setAttribute("t_short_array", new short[] { 4, -5 }); + element.setAttribute("t_long_array", new long[] { 6L, -7L }); + element.setAttribute("t_biginteger_array", new BigInteger[] { BigInteger.ONE, new BigInteger("-99") }); + element.setAttribute("t_byte_array", new byte[] { 8, -9 }); + element.setAttribute("t_float_array", new float[] { 1.5f, -2.5f }); + element.setAttribute("t_double_array", new double[] { 3.5, -4.5 }); + element.setAttribute("t_bigdecimal_array", new BigDecimal[] { BigDecimal.ONE, new BigDecimal("-0.5") }); + element.setAttribute("t_char_array", new char[] { 'a', 'é', '中' }); + // No null elements: Serialization.serializeStringArray does not support them + element.setAttribute("t_string_array", new String[] { "one", "two", "three" }); + + element.setAttribute("t_boxed_boolean_array", new Boolean[] { Boolean.FALSE, Boolean.TRUE }); + element.setAttribute("t_boxed_character_array", new Character[] { 'x', 'y' }); + } + + private static void setDynamicValues(org.gephi.graph.api.Element element, TimeRepresentation timeRepresentation) { + if (timeRepresentation == TimeRepresentation.INTERVAL) { + IntervalSet set = new IntervalSet(); + set.add(new Interval(1.0, 2.0)); + set.add(new Interval(3.0, 4.0)); + element.setAttribute("t_interval_set", set); + + IntervalBooleanMap booleanMap = new IntervalBooleanMap(); + booleanMap.put(new Interval(1.0, 2.0), true); + booleanMap.put(new Interval(3.0, 4.0), false); + element.setAttribute("t_interval_boolean", booleanMap); + + IntervalIntegerMap integerMap = new IntervalIntegerMap(); + integerMap.put(new Interval(1.0, 2.0), 10); + integerMap.put(new Interval(3.0, 4.0), -20); + element.setAttribute("t_interval_integer", integerMap); + + IntervalShortMap shortMap = new IntervalShortMap(); + shortMap.put(new Interval(1.0, 2.0), (short) 30); + element.setAttribute("t_interval_short", shortMap); + + IntervalLongMap longMap = new IntervalLongMap(); + longMap.put(new Interval(1.0, 2.0), 40L); + element.setAttribute("t_interval_long", longMap); + + IntervalByteMap byteMap = new IntervalByteMap(); + byteMap.put(new Interval(1.0, 2.0), (byte) 50); + element.setAttribute("t_interval_byte", byteMap); + + IntervalFloatMap floatMap = new IntervalFloatMap(); + floatMap.put(new Interval(1.0, 2.0), 6.5f); + element.setAttribute("t_interval_float", floatMap); + + IntervalDoubleMap doubleMap = new IntervalDoubleMap(); + doubleMap.put(new Interval(1.0, 2.0), 7.5); + element.setAttribute("t_interval_double", doubleMap); + + IntervalCharMap charMap = new IntervalCharMap(); + charMap.put(new Interval(1.0, 2.0), 'c'); + charMap.put(new Interval(3.0, 4.0), '中'); + element.setAttribute("t_interval_char", charMap); + + IntervalStringMap stringMap = new IntervalStringMap(); + stringMap.put(new Interval(1.0, 2.0), "alpha"); + stringMap.put(new Interval(3.0, 4.0), "beta"); + element.setAttribute("t_interval_string", stringMap); + } else { + TimestampSet set = new TimestampSet(); + set.add(1.0); + set.add(3.0); + element.setAttribute("t_timestamp_set", set); + + TimestampBooleanMap booleanMap = new TimestampBooleanMap(); + booleanMap.put(1.0, true); + booleanMap.put(3.0, false); + element.setAttribute("t_timestamp_boolean", booleanMap); + + TimestampIntegerMap integerMap = new TimestampIntegerMap(); + integerMap.put(1.0, 10); + integerMap.put(3.0, -20); + element.setAttribute("t_timestamp_integer", integerMap); + + TimestampShortMap shortMap = new TimestampShortMap(); + shortMap.put(1.0, (short) 30); + element.setAttribute("t_timestamp_short", shortMap); + + TimestampLongMap longMap = new TimestampLongMap(); + longMap.put(1.0, 40L); + element.setAttribute("t_timestamp_long", longMap); + + TimestampByteMap byteMap = new TimestampByteMap(); + byteMap.put(1.0, (byte) 50); + element.setAttribute("t_timestamp_byte", byteMap); + + TimestampFloatMap floatMap = new TimestampFloatMap(); + floatMap.put(1.0, 6.5f); + element.setAttribute("t_timestamp_float", floatMap); + + TimestampDoubleMap doubleMap = new TimestampDoubleMap(); + doubleMap.put(1.0, 7.5); + element.setAttribute("t_timestamp_double", doubleMap); + + TimestampCharMap charMap = new TimestampCharMap(); + charMap.put(1.0, 'c'); + charMap.put(3.0, '中'); + element.setAttribute("t_timestamp_char", charMap); + + TimestampStringMap stringMap = new TimestampStringMap(); + stringMap.put(1.0, "alpha"); + stringMap.put(3.0, "beta"); + element.setAttribute("t_timestamp_string", stringMap); + } + } + + private static void setCollectionValues(org.gephi.graph.api.Element element) { + List list = new ArrayList<>(); + list.add("first"); + list.add("second"); + element.setAttribute("t_list", list); + // t_set and t_map stay null, see addCollectionColumns + } + + // Serialization helpers + + /** + * Serializes the model through the production path, exactly as the fixture files on disk were written. + * + * @param graphModel model to serialize + * @return serialized bytes + * @throws IOException if an io error occurs + */ + public static byte[] serialize(GraphModel graphModel) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos)) { + GraphModel.Serialization.write(dos, graphModel); + } + return baos.toByteArray(); + } + + /** + * Regenerates every fixture of the current minor. + * + * @param args optional single argument, the fixture root directory (defaults to {@link #FIXTURE_ROOT}) + * @throws IOException if an io error occurs + */ + public static void main(String[] args) throws IOException { + File root = new File(args.length > 0 ? args[0] : FIXTURE_ROOT, CURRENT_MINOR); + if (!root.exists() && !root.mkdirs()) { + throw new IOException("Can't create the fixture folder " + root.getAbsolutePath()); + } + System.out.println("Writing " + CURRENT_MINOR + " fixtures to " + root.getAbsolutePath()); + for (Map.Entry entry : buildAll().entrySet()) { + File file = new File(root, entry.getKey() + FILE_EXTENSION); + byte[] bytes = serialize(entry.getValue()); + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(bytes); + } + System.out.println(" " + file.getName() + " (" + bytes.length + " bytes)"); + } + + // Cheap self-check: every declared type must actually be supported + for (Class type : new Class[] { Boolean.class, Integer.class, Short.class, Long.class, BigInteger.class, Byte.class, Float.class, Double.class, BigDecimal.class, Character.class, String.class, Instant.class, boolean[].class, int[].class, short[].class, long[].class, BigInteger[].class, byte[].class, float[].class, double[].class, BigDecimal[].class, char[].class, String[].class, List.class, java.util.Set.class, Map.class }) { + if (!AttributeUtils.isSupported(type)) { + throw new IllegalStateException("Type no longer supported: " + type); + } + } + System.out.println("Done."); + } +} diff --git a/src/test/resources/serialization/0.4/graph-basic.graphstore b/src/test/resources/serialization/0.4/graph-basic.graphstore new file mode 100644 index 0000000000000000000000000000000000000000..052bdabbf87d42a60e2e1ca558ab5bbd7f1c6b80 GIT binary patch literal 831 zcmdT?O-sW-5Zxx-Ogs8``LNDFOeSAFT>% z{X#GP2`?Ix8cT%MgEx0zXLfmSn0YgFZ8#i_29Vt|4h(7=9iW=qvpVpHuTpovX|tP@ zu2vLHU$4|IuDRgcX5M|@7cw8=eMX82FQFz-5e)~h*zExY=CmvdJ;P~H!?Eag*JL7a zmI<6{o`YDcS8GaL-^DXZLOh26+eVYwfXkta5y(sek3ev(HtRDNQhFG;M%#(egaIti zs>?lcSf5TA8gmiPKj1C3uIcl;QA}W|f7T6%Bnntn|@kRcf7%hNCO z#4BiZV)Hm9@6afiWWl2Wkd^(@bWqtqn zO~^0s_qmMcc_N1-DIyifH>nVj3TP}zQh-ZJar`M@7E~f*f@MxkMlXcqyGhDe<}0iQ G4SfQda}%im literal 0 HcmV?d00001 diff --git a/src/test/resources/serialization/0.4/graph-parallel.graphstore b/src/test/resources/serialization/0.4/graph-parallel.graphstore new file mode 100644 index 0000000000000000000000000000000000000000..6f39a5fe8a3a880a5a3980db579d07a7ccdd2944 GIT binary patch literal 887 zcmdT?O-sW-5M8s~O;Qi-(W561X$d6YrHJ51gg|Rcn<5@lx=tf)6WXYFDFOeSAFT>% z{X#GP2`>_pHr5EO2XF4e&g}Bu@aFBvtNs39(1Y~8d1%T_vkh{^?O1L2!_%gHP;0VV z(a;N8#aJtpE^oMC-K9Q!-{;5Yxsb!&oQFM5#|UuE>;T@g?Hua&V8QNy49sa*GIUI* zA)AgRZ`*Yy0B6;K)9V)?l*+}DRyKCY6oCkH2uRbcu_lmw7-9rEl@N&FU8}{q%mvX1 z6W44xF&f{4rD?j{V~2I+38TVXgz+QU(#sWNb~jobnE21S0eZwNXt+RkMloiN@Wa#b z{|7dp9D05Jgs8``LN2?76{-wJB| zLNERaFB+2?ON7>gH+NuXc6o1@c{6kcA$0CRe#h7|sBQFsYVN@5!5_XV{oS_BZZf)B zQ#5_G*5EF#x$xZP-h&_zav#xsLdq#WrKV6Bop~_d9{>gBbSw%3!|71NvFK*sWFl~u z37l%4g4k%*8%k5(##3rSJcj_=Mw{7ytC33($W#K4Kya-t8!{I%dK9=u*GbR>4;H7@ zjua^v!LSw*&6$VIN}dYzeXO)5RUWZ>6dxp zCA2)YS)fR!3Fs2zNBj(=Qg5wQl~>?f`nq@oxEF4Qj8#64fJCdZAfr(+Tg(>nL2&pc z6zBN+LeBSnkwcOclS<^9REkL@G?FAK#HFPq{unR|E0Hn5GN-2F7eeygIAtR96;{KB FJ^=+o5k~+3 literal 0 HcmV?d00001 diff --git a/src/test/resources/serialization/0.5/graph-parallel.graphstore b/src/test/resources/serialization/0.5/graph-parallel.graphstore new file mode 100644 index 0000000000000000000000000000000000000000..28a6e22fe9d1043ba238077edf7412ea04d5dc96 GIT binary patch literal 888 zcmdT?O-sW-5M8s~O*+b!)HLVYg{r zt&}uEkX17fV9K&u) zhHXllR*MP1nJwVd))@%3dbL)n>)RwjA;KI2(lMH>1Ed^=7=b1d0uj7xc3Gdfkk-S* zHM(|;#t&d2Ntb(Qv%WN8Xv{?zKavf#uIbaeDYby5|5-Oc510vU7wFa~#>^3Zcsd?{ zzy?%Aug|~yCs{;Gqdg52+B5-O5%NS{fUZ{?tL4%g;D)}IJ_gbYCPOAFjxwO(sLab~ zSjZGI`JC?`z4L`x{E*Ljo+off6eE&Czeq|%QqWKo#Q>KQMK^@x X`%%eQ-6XAt_UO@*hqNrD;H3znHbS7arAZNwDOskGK0+H6FD2lY^R1xP z7kcqac+r^DSR%9@ytxZIv&;WC%wyRKLg;)5*&TDwWNotxOn3Ws7yj^6>g_h${3c~+ z6;(G@D>Zy^je~WYdG~!^%zT9BiBL><2`zz&=xhk{y*@DDPRnM{H=PzU9h+_TEG_}I zEx?*}3R10Jt*Lcmn@-6I5sZMg%_eUHEr%us2vY%qKy>X6A8;2^M(DU^$BAL$5EiH9 zjQKIns}wevKSq5{&cZ>6dxZ zCA2)=vqWK=CZbD9AL%m)rD|iXtiA$i80*pz&~ETE6snQP10nn>3kn+L)A@8R>-&dq zVt$U?=Q5t>Nf^m;1QmpDs1QK~G?HaG&?V)#{un3^GEt~N6|5zr8$!z6IAkpH71e@* FJ^=(t5k>$2 literal 0 HcmV?d00001 diff --git a/src/test/resources/serialization/0.6/graph-parallel.graphstore b/src/test/resources/serialization/0.6/graph-parallel.graphstore new file mode 100644 index 0000000000000000000000000000000000000000..f9092ad0b21e3102ae62571a6afbfb873fb1355a GIT binary patch literal 888 zcmdT?O-sW-5FK~jOA<){=rijOsuG2`{gc=nuCE%a)TfuKd zFa8NH5|bJ=LiFIxUD%mj-W%SV9d!->P6jx>VQpJ#$LgV4_xg4Z|M0ZrY&ARLGGiL$ zV%?lCSIOxG3D;HbH3$Of{XCQl)F1JwPYeo3n^qs`y5kh!cz_d5A5|1?+g7n}xoy>Q zZFR+I2^opqLSnRzP_EW0)nd(DV<`%O5L;TvJfQTBEgQAE`Lmm13q|Iixk0`oY;1eTSBEjt9Qj*M-vm1vA}jTocx4M4jY VmP}NRkvikj7gA3{o--q~{subOAnyPG literal 0 HcmV?d00001 diff --git a/src/test/resources/serialization/0.7/graph-basic.graphstore b/src/test/resources/serialization/0.7/graph-basic.graphstore new file mode 100644 index 0000000000000000000000000000000000000000..7359f3a6fb0b7b7ea92578c7d1126691dbb2f334 GIT binary patch literal 832 zcmdT?O-sW-5Zxx-Ogs8``LN2?76{-wJB| zLNERaFB+2?ON7>gH+NuXc6o1@c{6kcA$0CRe#h7|sBQFsYVN@5!5_XV{oS_BZZf)B zQ#5_G*5EF#x$xZP-h&_zav#xsLdq#WrKV6Bop~_d9{>gBbSw%3!|71NvFK*sWFl~u z37l%4g4k%*8%k5(##3rSJcj_=Mw{7ytC33($W#K4Kya-t8!{I%dK9=u*GbR>4;H7@ zjua^v!LSw*&6$VIN}dYzeXO)5RUWZ>6dxp zCA2)YS)fR!3Fs2zNBj(=Qg5wQl~>?f`nq@oxEF4Qj8#64fJCdZAfr(+Tg(>nL2&pc z6zBN+LeBSnkwcOclS<^9REkL@G?FAK#HFPq{unR|E0Hn5GN-2F7eeygIAtR96;{KB FJ^=+o5k~+3 literal 0 HcmV?d00001 diff --git a/src/test/resources/serialization/0.7/graph-parallel.graphstore b/src/test/resources/serialization/0.7/graph-parallel.graphstore new file mode 100644 index 0000000000000000000000000000000000000000..28a6e22fe9d1043ba238077edf7412ea04d5dc96 GIT binary patch literal 888 zcmdT?O-sW-5M8s~O*+b!)HLVYg{r zt&}uEkX17fV9K&u) zhHXllR*MP1nJwVd))@%3dbL)n>)RwjA;KI2(lMH>1Ed^=7=b1d0uj7xc3Gdfkk-S* zHM(|;#t&d2Ntb(Qv%WN8Xv{?zKavf#uIbaeDYby5|5-Oc510vU7wFa~#>^3Zcsd?{ zzy?%Aug|~yCs{;Gqdg52+B5-O5%NS{fUZ{?tL4%g;D)}IJ_gbYCPOAFjxwO(sLab~ zSjZGI`JC?`z4L`x{E*Ljo+off6eE&Czeq|%QqWKo#Q>KQMK^@x X`%%eQgs8``LN2?76{-wJB| zLNERaFB+2?ON7>gH+NuXc6o1@c{6kcA$0CRe#h7|sBQFsYVN@5!5_XV{oS_BZZf)B zQ#5_G*5EF#x$xZP-h&_zav#xsLdq#WrKV6Bop~_d9{>gBbSw%3!|71NvFK*sWFl~u z37l%4g4k%*8%k5(##3rSJcj_=Mw{7ytC33($W#K4Kya-t8!{I%dK9=u*GbR>4;H7@ zjua^v!LSw*&6$VIN}dYzeXO)5RUWZ>6dxp zCA2)YS)fR!3Fs2zNBj(=Qg5wQl~>?f`nq@oxEF4Qj8#64fJCdZAfr(+Tg(>nL2&pc z6zBN+LeBSnkwcOclS<^9REkL@G?FAK#HFPq{unR|E0Hn5GN-2F7eeygIAtR96;{KB FJ^=+o5k~+3 literal 0 HcmV?d00001 diff --git a/src/test/resources/serialization/0.8/graph-parallel.graphstore b/src/test/resources/serialization/0.8/graph-parallel.graphstore new file mode 100644 index 0000000000000000000000000000000000000000..28a6e22fe9d1043ba238077edf7412ea04d5dc96 GIT binary patch literal 888 zcmdT?O-sW-5M8s~O*+b!)HLVYg{r zt&}uEkX17fV9K&u) zhHXllR*MP1nJwVd))@%3dbL)n>)RwjA;KI2(lMH>1Ed^=7=b1d0uj7xc3Gdfkk-S* zHM(|;#t&d2Ntb(Qv%WN8Xv{?zKavf#uIbaeDYby5|5-Oc510vU7wFa~#>^3Zcsd?{ zzy?%Aug|~yCs{;Gqdg52+B5-O5%NS{fUZ{?tL4%g;D)}IJ_gbYCPOAFjxwO(sLab~ zSjZGI`JC?`z4L`x{E*Ljo+off6eE&Czeq|%QqWKo#Q>KQMK^@x X`%%eQ0+4z|hyy1MTq41t5NInNr9i2w(n>8tzySfJqUM{IJ)1bqIw=PPK6q!w z-#6c5zM1)Xjb4P%rzPC_ih0U395aiJF}Gl6vEQMcxdJw@mCx7)E|~d@Vdibaoyl88 zBb9Zn!YR`k$eo;YtRrUri*RaWd~o#nzG3yk=PG=@jQ&+FmqXF}qoLswov= z?L;oujXo*iO|_;U$)&Bx4y@W~tftcQIDB;Uxub)l<1gtoyha+#r#t4PB0}YZ z?u3)eV$7dhx~YUCRq0_6pu)`%h>e9;o*q|hF5&f5aoTh0Wn3igw^B6L~O9qTpnVpbcAdxkxfzA z7G7*z+S?WKYl!?9m4{4|s^j9Go{(Nkqz_Z+wQ|{Uof>08b6p8-{)m^zSG#-UI<~fxv3TAKvDXueCS{!nGrrN_|!pDdUtk5ujIB{&S&L&&z3a0|U0s4O8hLR%m+ZRYtyh_-t~ znI~vzHXTf3E7JDwV$;w@Sf9X40k#g{r3mXAcmZ2i2)sb6 zs|8*Puyp`0#n-xk7qE4OzzejxFuZz!7x1-C-~~Q?xWG$cb(O#i_$q}_xxXI(yg;Kq zUsu$|P#?hS4?`aY@B)hZKx=*id$~adw`G7AaP%JrUVl8g3g87A^+D9D0$xB-A6Z&0 z@B)q2#URxpUdXpg0xz(rk6f)1c!5uS#8CryyV13)4|o&W&~UReq3ZCZhi)d+gciP; z&=P^*ezZT(s4L1+x`b3iA*L%7)aKwtUH_&TH<+$l=pcUz+7ZUlO0R>yIvP@ui6(Iq z9cKSnb|;(RO)`9NGaH@Bn$z~wjjllNFYJdTd!S^P_gmTXu&ST13q?0rw5D>|G}pJ; zo2%QKXV3ldryh9~hv~F%-bLcvF#A7&o9KJzm}8x?obPZLUZ@U^r;ZGMkDCWi7IJy( z*`sE`E{4xzJqz!RJN48K-oiohB01K1y-Th5xSsi>w$6PR+FJf~^AGRL-P*RdMS1;B z^tTU+*MA!6ZTze)Wb~dxX!l%z3GZn@Z;!wA+Etu?x3L`^!wnhBadNRc$~$i;XXh0N z;o7-*JreKH6r>!`kW#*nv~Cpz%J=$J6fECOse2V=ukz+0HT?TNwRaAoxfnuSsz~YF zT4pGv?okx9N4cjAX|MoyMmi2_S}tp8?&+NFW(t%)q4Onzurj)h6b}iO^+(tdBt!$Vk47Q1tx!7taQa literal 0 HcmV?d00001 diff --git a/src/test/resources/serialization/0.8/graph-types-timestamp.graphstore b/src/test/resources/serialization/0.8/graph-types-timestamp.graphstore new file mode 100644 index 0000000000000000000000000000000000000000..2bb6c50c3e69957e45e0ae00961f15ba448f019b GIT binary patch literal 7077 zcmeI0&u<$=6vtoG8(#x0PDiN&m3E$-M-!Rx8xtZrqsArm|21~I1RAR~U| zF0HOuLtr2hIgu;qG!c5k&7+XJBa=~LLmk4_RE7!;*Y1Q1B<~P9lcgh}j4@-*>N(63 z;w|9|rEq67%&e(@vo$r7)l2$}1*_z22ydvzI=heugsqrxnLcx5!t74CK}{SJW=|&d zp`w6?D-Zf4`k=SMD=Jh$B&?;_kJK$zQ{a?dD(Q20X@?jhR>AV|VJd>)f&RK zjo7BBZ7UZWw{|y${TgCFN$sK1M0MQU)e_cgiS;3Bz1Ar^?&HxXJl9q5#^YmND(n-% zi45)Fc1sMgo&+BfsZsgasX3a$egm-|rS?%FL+-GoEj%6}9>b3Gxa?f%x%!0X#`^TQ zBx~srP1@<%EiuF<68wOeE|Z=;U1wW(JPJDIHFX>oEZ_zrt}{kPY;t@g?`|>RV`K_U zXd8up?NQ<*Ecb&L<8eC;&@=(NQ$%^9f;)X2 z6MVae6B5=!oEENe#a`>xDS~bB7mV-3?qF-2nX!YTzDoppvVwQ|$ci2=?l5i6F!a2s z>k&cX6`b%9gq%qah(XU8R}9i4Vm#$9#t^u;W4LNV_W@krC89j-uUzy5bBBS=%~mr% z)I9n`psf|$5RN&-<|+|+{%~E7h_cOJ(GI{M56FgXF4&a~>l1OF@t4gcJv!Zy2*j9W zi^2?C#faMlVVtzgoHho((5+8}Lko5ab`iGi72HaX4r)s{j<6Wm%<4sc5vCn3Q#KQ{ zJiCr$un}$lud!?Jv|-{bl=OLeb)YbiYh$YO2gGi)^$Wy6cn*x_TOD0G!NE&`7`V=X zk$(roz;P=BVjwv;K04>-4a9_S9l^*hBr;)J4x~hHgb>|qPxlk*P~+fwB8Kc8fY}%k z*D-!WL<|L42@wO^Iws5>eww6>!5%~mILlywBn;Wj|TlyQ9%F>vh)i5PHp zwM0w_w*exieA^Ha1J|yQhyiDprq?JD1K&1C!~n1lmxw9Nu9Ap>ZbceP0eD#=23Ga^ zN@E#F^+UuyA@$)9F@UNc{9Zi~1FZh@MC_ARS4G6Ys(z?@l|&4n>Ze?*Ct_gL#@NJG zb{+pNPsBi~eu}ngA_ji-lZ^)v_oD099`GtP(i0f?8D0d#CrQ8I8fG`iHg@d~AVre< zQGcLWm89hcBWxS$k!yqTLHcDRfvemSyrimMwcv`6K5Ne0tPgcaR!^9vvK1^FGlhJH+w&J>w>Qt9`~43!`U(!y zVa&W0P4GbM&j)U%Z?WS!OjYEBx~eyh4>xm{Oi)$MF|qPer{2XCi)Z!N&6KfRqrFE zS4M&Ay&)L|t9MiKUP;<3y>U5$j z=OINY5{b)d3iYGE0Bkh&uK)l5 literal 0 HcmV?d00001 diff --git a/src/test/resources/serialization/0.8/graph-views.graphstore b/src/test/resources/serialization/0.8/graph-views.graphstore new file mode 100644 index 0000000000000000000000000000000000000000..8fa2ceac75115a663a5136bc3f93fd37d2c0e200 GIT binary patch literal 1408 zcmdUvO>fgM7{^_^wv!-Hjyv|mp@^tS!)2<3GFqfa8>`w(h+|64)1bDgx@;4dl8*ek`gq}^|(*58tFbBa1%uYNBNAM3* zjp(2|5U+LL+i7-ucc+D)zrtxdEqz-o7JM0>XqzM{BqYHeK|s2}1jxN8+C)#Mun|qb z1TpT1CQO2H-wei~c`ND(18~>_?DdX8YqfV<&9;As=46NnMnDHaR}6q|%Wef=xj>o( zkHewZ7cuC*9K^wJyn^v5T+YiYelQmM<{8luF(TwWz3H_(KCmMZLO=f9A49R>`mS~^ zgQZ;vOB><gro3k~W=* zUQjw`V5m~N8pQDgjA0Ot%4*^xu*UA*)^_tVkUjs#MdK0RQI&dBcEDs+maC`=#q{c` zZz`2aDM=EoLf&0b1B@yaNmxt~Rz_IJQiQdhu(=}ajIfc76=JVPYMMf78B#+wQAlk) z;^+!-GQ>f4Q6Ww~@))qFQbsN873B~i>#aKH%5kN}Vw`0f3#FOoifpcNi>QU{R77$` zHBD!ksFsR~xgtl`dA6OCis&Drm)yfA7*mbSG`GNK%qa0?qM)Co%0C#fXe;fLBm K#W~G^>*yCRbF-5G literal 0 HcmV?d00001 diff --git a/src/test/resources/serialization/README.md b/src/test/resources/serialization/README.md new file mode 100644 index 00000000..2af87989 --- /dev/null +++ b/src/test/resources/serialization/README.md @@ -0,0 +1,51 @@ +# Serialization golden fixtures + +Golden files used by `org.gephi.graph.impl.SerializationCompatibilityTest` to guard the on-disk serialization format. + +## Layout + +One directory per minor version: + +``` +serialization/ + 0.4/ graph-basic, graph-parallel + 0.5/ graph-basic, graph-parallel + 0.6/ graph-basic, graph-parallel + 0.7/ graph-basic, graph-parallel + 0.8/ graph-basic, graph-parallel, graph-types-timestamp, graph-types-interval, graph-views +``` + +Patch versions are not tracked separately; the format is stable within a minor. 0.6.13 and 0.6.14 are byte-identical, +so 0.6 holds one copy. + +Each file is a raw `GraphModel.Serialization.write(...)` dump over a `DataOutputStream`, with no compression. Read them +with `GraphModel.Serialization.read(new DataInputStream(...))`. + +## Contract + +A minor bump may change the byte format. Older formats must remain readable. + +1. Every fixture from 0.4 up deserializes and holds the expected content. +2. For the current minor, serializing the model built by `SerializationFixtureGenerator` reproduces the committed + bytes. Older minors are exempt, since graphstore no longer writes those formats. +3. The same content serializes to the same bytes regardless of build order. + +## Regenerating + +Regeneration applies to the current version only. The legacy directories are historical artifacts and are never +rewritten. + +``` +mvn -q test-compile +mvn -q dependency:build-classpath -Dmdep.outputFile=target/test-cp.txt +java -cp "target/classes:target/test-classes:$(cat target/test-cp.txt)" \ + org.gephi.graph.impl.SerializationFixtureGenerator +``` + +The fixture root defaults to `src/test/resources/serialization` and can be passed as the single argument. + +A failing byte-pin means either a bug or an intended format change. An intended change is committed together with the +regenerated fixtures, plus a `Serialization.VERSION` bump if it breaks read compatibility. + +When the project moves to a new minor: create its directory, generate its fixtures, bump +`SerializationFixtureGenerator.CURRENT_MINOR`, and add the minor to `SerializationCompatibilityTest.ALL_MINORS`. \ No newline at end of file From 121a0c6f0c745337d55eaf121ddf886ff9f0c405 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 22 Aug 2026 22:11:06 +0200 Subject: [PATCH 263/271] Fix time index reference counts (#288) * Rebuild the time index on read instead of restoring it TimeIndexStore.countMap came back doubled after a load. The time store is read before the nodes and edges, which restored the reference counts from the stream, and inserting the elements then re-indexed their time references and incremented the same counts again. Counts never reached zero on remove, so time values stayed in the index once nothing referenced them. The index is derived state: nothing outside the block references its slot ids, and TimeIndexStore.index/clear already maintain one count per element reference. Reading now parses the block to advance the stream and discards it, letting element insertion rebuild the index. Loading a store whose counts were inflated by ElementImpl.setTimeAttribute yields canonical counts and drops time values nothing references. The write path is unchanged, so the byte format and the golden fixtures stay as they are. testTimestampStore and testIntervalStore asserted that the block round-trips its own state with no elements present. They now assert it is consumed in full and carries nothing. * Count one time index reference per dynamic attribute time ElementImpl.setTimeAttribute passed the whole map to updateIndex, so each put re-counted every time already in it, including puts that only overwrote an existing time. removeTimeAttribute decrements one at a time, so the counts drifted upward without bound and time values stayed in the index once nothing referenced them. AttributesImpl.setAttribute now reports whether the time was new, and setTimeAttribute passes that single time to the index, matching addTime and removeTimeAttribute. Dynamic attribute columns are never value indexed, so the column index is unaffected by the narrower value. The counts written to disk are now canonical, which moves one byte in each of the two 0.8 fixtures holding dynamic times. The layout is unchanged and the field is ignored on read. * Remove unecessary comment * Reference #288 in the rebuild-on-read test * Write the time index block empty The block is derived state: reading rebuilds it from the nodes and edges. The layout is kept, since earlier versions read it positionally, and the fields are written empty. Serialized bytes no longer depend on slot allocation history, so the same content always serializes to the same bytes. The deserializers read each field through its declared type again, so a mismatched block fails at the offending field. Co-Authored-By: Claude Opus 5 (1M context) * Cover the empty index block and re-setting a held time Assert that index content leaves no trace in the serialized bytes, and set a time the dynamic map already holds so the round-trip count comparison exercises the write path. Co-Authored-By: Claude Opus 5 (1M context) * Document that mutating a live TimeMap or TimeSet bypasses the index getAttribute returns the instance held by the element. The types carry no reference to the store, so putting or removing on one directly leaves the time index stale. Co-Authored-By: Claude Opus 5 (1M context) * Fold the empty index block assertion into the store tests testTimestampStore and testIntervalStore already built an index with content and serialized it, so they cover the write side too. Drops the two separate tests and the assertions that restated isEmpty. Co-Authored-By: Claude Opus 5 (1M context) --------- Co-authored-by: Claude Opus 5 (1M context) --- .../java/org/gephi/graph/api/Element.java | 10 + .../org/gephi/graph/api/types/TimeMap.java | 5 + .../org/gephi/graph/api/types/TimeSet.java | 5 + .../org/gephi/graph/impl/AttributesImpl.java | 10 +- .../org/gephi/graph/impl/ElementImpl.java | 6 +- .../org/gephi/graph/impl/Serialization.java | 56 ++-- .../graph/impl/IntervalIndexStoreTest.java | 29 ++ .../impl/SerializationCompatibilityTest.java | 6 +- .../gephi/graph/impl/SerializationTest.java | 259 +++++++++++++++++- .../graph/impl/TimestampIndexStoreTest.java | 48 ++++ .../0.8/graph-types-interval.graphstore | Bin 7265 -> 7226 bytes .../0.8/graph-types-timestamp.graphstore | Bin 7077 -> 7017 bytes 12 files changed, 379 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/Element.java b/src/main/java/org/gephi/graph/api/Element.java index f64e84ee..45746085 100644 --- a/src/main/java/org/gephi/graph/api/Element.java +++ b/src/main/java/org/gephi/graph/api/Element.java @@ -39,6 +39,11 @@ public interface Element extends ElementProperties { /** * Gets the attribute for the given key. + *

+ * For dynamic columns the returned {@link org.gephi.graph.api.types.TimeMap TimeMap} or + * {@link org.gephi.graph.api.types.TimeSet TimeSet} is the instance held by this element. Mutating it directly + * leaves the time index stale: use the setAttribute and removeAttribute methods that take + * a time instead. * * @param key column's key * @return attribute value, or null @@ -47,6 +52,11 @@ public interface Element extends ElementProperties { /** * Gets the attribute for the given column. + *

+ * For dynamic columns the returned {@link org.gephi.graph.api.types.TimeMap TimeMap} or + * {@link org.gephi.graph.api.types.TimeSet TimeSet} is the instance held by this element. Mutating it directly + * leaves the time index stale: use the setAttribute and removeAttribute methods that take + * a time instead. * * @param column column * @return attribute value, or null diff --git a/src/main/java/org/gephi/graph/api/types/TimeMap.java b/src/main/java/org/gephi/graph/api/types/TimeMap.java index ec5298be..97942b52 100644 --- a/src/main/java/org/gephi/graph/api/types/TimeMap.java +++ b/src/main/java/org/gephi/graph/api/types/TimeMap.java @@ -22,6 +22,11 @@ /** * Interface that defines the functionalities both timestamp and interval map have. + *

+ * Once a map is set on an element's dynamic column, the graph store maintains a time index over its keys. Calling + * {@link #put} or {@link #remove} on that instance bypasses the index and leaves it stale. Populate a map before + * setting it on an element, and go through the element's setAttribute and removeAttribute + * methods afterwards. * * @param key type * @param value type diff --git a/src/main/java/org/gephi/graph/api/types/TimeSet.java b/src/main/java/org/gephi/graph/api/types/TimeSet.java index d2393cc0..5ffa27e4 100644 --- a/src/main/java/org/gephi/graph/api/types/TimeSet.java +++ b/src/main/java/org/gephi/graph/api/types/TimeSet.java @@ -20,6 +20,11 @@ /** * Interface that defines the functionalities both timestamp and interval set have. + *

+ * Once a set is attached to an element, the graph store maintains a time index over its keys. Calling {@link #add} or + * {@link #remove} on that instance bypasses the index and leaves it stale. Populate a set before setting it on an + * element, and go through the element's addTimestamp/addInterval and + * removeTimestamp/removeInterval methods afterwards. * * @param key type */ diff --git a/src/main/java/org/gephi/graph/impl/AttributesImpl.java b/src/main/java/org/gephi/graph/impl/AttributesImpl.java index 9d6fe09a..ab7a8eeb 100644 --- a/src/main/java/org/gephi/graph/impl/AttributesImpl.java +++ b/src/main/java/org/gephi/graph/impl/AttributesImpl.java @@ -147,7 +147,12 @@ private Object ensureSize(int index) { return null; } - protected Object setAttribute(Column column, Object value, Object timeObject) { + /** + * Puts a value at the given time in the column's map, creating the map if needed. + * + * @return true if the time was not already in the map + */ + protected boolean setAttribute(Column column, Object value, Object timeObject) { int index = column.getIndex(); Object oldValue = null; synchronized (this) { @@ -165,8 +170,7 @@ protected Object setAttribute(Column column, Object value, Object timeObject) { dynamicValue = (TimeMap) oldValue; } - dynamicValue.put(timeObject, value); - return dynamicValue; + return dynamicValue.put(timeObject, value); } } diff --git a/src/main/java/org/gephi/graph/impl/ElementImpl.java b/src/main/java/org/gephi/graph/impl/ElementImpl.java index ea0295a8..f69c4612 100644 --- a/src/main/java/org/gephi/graph/impl/ElementImpl.java +++ b/src/main/java/org/gephi/graph/impl/ElementImpl.java @@ -271,8 +271,10 @@ private void setTimeAttribute(Column column, Object value, Object timeObject) { checkReadOnlyColumn(column); checkDynamicType(column, value); - Object newValue = attributes.setAttribute(column, value, timeObject); - updateIndex(column, null, newValue); + // Only a time the map did not already hold is a new reference for the time index. Passing the whole map would + // re-count every time in it, as removeTimeAttribute decrements one at a time. + boolean isNewTime = attributes.setAttribute(column, value, timeObject); + updateIndex(column, null, isNewTime ? timeObject : null); } private void updateIndex(Column column, Object oldValue, Object newValue) { diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 07d630be..2cd7917e 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -24,7 +24,6 @@ import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; import it.unimi.dsi.fastutil.chars.CharArrayList; import it.unimi.dsi.fastutil.chars.CharOpenHashSet; -import it.unimi.dsi.fastutil.doubles.Double2IntMap; import it.unimi.dsi.fastutil.doubles.Double2ObjectOpenHashMap; import it.unimi.dsi.fastutil.doubles.DoubleArrayList; import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet; @@ -992,11 +991,13 @@ private IntervalMap deserializeIntervalMap(final DataInput is) throws IOExceptio private void serializeTimestampIndexStore(final DataOutput out, final TimestampIndexStore timestampIndexStore) throws IOException { serialize(out, timestampIndexStore.elementType); - serialize(out, timestampIndexStore.length); - serialize(out, timestampIndexStore.getMap().keySet().toDoubleArray()); - serialize(out, timestampIndexStore.getMap().values().toIntArray()); - serialize(out, timestampIndexStore.garbageQueue.toIntArray()); - serialize(out, timestampIndexStore.countMap); + // The time index is derived state: inserting the nodes and edges rebuilds it. These fields are written empty to + // keep the block layout, which earlier versions read positionally. + serialize(out, 0); + serialize(out, new double[0]); + serialize(out, new int[0]); + serialize(out, new int[0]); + serialize(out, new int[0]); } private TimestampIndexStore deserializeTimestampIndexStore(final DataInput is) throws IOException, ClassNotFoundException { @@ -1009,35 +1010,26 @@ private TimestampIndexStore deserializeTimestampIndexStore(final DataInput is) t timestampIndexStore = (TimestampIndexStore) model.store.timeStore.edgeIndexStore; } + // The time index is derived state: inserting the nodes and edges rebuilds it. These fields are read to advance + // the stream and discarded. The casts check each field's type. See serializeTimestampIndexStore for the layout. int length = (Integer) deserialize(is); - double[] doubles = (double[]) deserialize(is); - int[] ints = (int[]) deserialize(is); + double[] timestamps = (double[]) deserialize(is); + int[] timeIndices = (int[]) deserialize(is); int[] garbage = (int[]) deserialize(is); int[] counts = (int[]) deserialize(is); - timestampIndexStore.length = length; - for (int i : garbage) { - timestampIndexStore.garbageQueue.add(i); - } - Double2IntMap m = timestampIndexStore.getMap(); - for (int i = 0; i < ints.length; i++) { - m.put(doubles[i], ints[i]); - } - timestampIndexStore.countMap = counts; return timestampIndexStore; } private void serializeIntervalIndexStore(final DataOutput out, final IntervalIndexStore intervalIndexStore) throws IOException { serialize(out, intervalIndexStore.elementType); - serialize(out, intervalIndexStore.length); - serialize(out, intervalIndexStore.getMap().size()); - for (Map.Entry entry : intervalIndexStore.getMap().entrySet()) { - serialize(out, entry.getKey()); - serialize(out, entry.getValue()); - } - serialize(out, intervalIndexStore.garbageQueue.toIntArray()); - serialize(out, intervalIndexStore.countMap); + // The time index is derived state: inserting the nodes and edges rebuilds it. These fields are written empty to + // keep the block layout, which earlier versions read positionally. The map is written with a zero entry count. + serialize(out, 0); + serialize(out, 0); + serialize(out, new int[0]); + serialize(out, new int[0]); } private IntervalIndexStore deserializeIntervalIndexStore(final DataInput is) throws IOException, ClassNotFoundException { @@ -1050,23 +1042,17 @@ private IntervalIndexStore deserializeIntervalIndexStore(final DataInput is) thr intervalIndexStore = (IntervalIndexStore) model.store.timeStore.edgeIndexStore; } + // The time index is derived state: inserting the nodes and edges rebuilds it. These fields are read to advance + // the stream and discarded. The casts check each field's type. See serializeIntervalIndexStore for the layout. int length = (Integer) deserialize(is); int mapSize = (Integer) deserialize(is); - - Interval2IntTreeMap map = intervalIndexStore.getMap(); for (int i = 0; i < mapSize; i++) { - Interval key = (Interval) deserialize(is); - Integer value = (Integer) deserialize(is); - map.put(key, value); + Interval interval = (Interval) deserialize(is); + Integer timeIndex = (Integer) deserialize(is); } int[] garbage = (int[]) deserialize(is); int[] counts = (int[]) deserialize(is); - intervalIndexStore.length = length; - for (int i : garbage) { - intervalIndexStore.garbageQueue.add(i); - } - intervalIndexStore.countMap = counts; return intervalIndexStore; } diff --git a/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java b/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java index 9c25d165..1e38791b 100644 --- a/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/IntervalIndexStoreTest.java @@ -427,6 +427,35 @@ public void testSetAttribute() { Assert.assertFalse(store.contains(new Interval(3.0, 4.0))); } + @Test + public void testSetAttributeIntervalCounts() { + Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); + GraphModelImpl graphModel = new GraphModelImpl(config); + IntervalIndexStore store = (IntervalIndexStore) graphModel.store.timeStore.nodeIndexStore; + + Column col = graphModel.store.nodeTable.addColumn("col", IntervalStringMap.class); + NodeImpl nodeImpl = (NodeImpl) graphModel.store.factory.newNode("0"); + graphModel.store.addNode(nodeImpl); + + Interval first = new Interval(1.0, 2.0); + Interval second = new Interval(3.0, 4.0); + + nodeImpl.setAttribute(col, "foo", first); + nodeImpl.setAttribute(col, "bar", second); + // Overwriting an existing interval is not a new reference + nodeImpl.setAttribute(col, "baz", first); + + Assert.assertEquals(store.size(), 2); + Assert.assertEquals(store.countMap[(Integer) store.timeSortedMap.get(first)], 1); + Assert.assertEquals(store.countMap[(Integer) store.timeSortedMap.get(second)], 1); + + nodeImpl.removeAttribute(col, first); + Assert.assertFalse(store.contains(first)); + nodeImpl.removeAttribute(col); + Assert.assertEquals(store.size(), 0); + Assert.assertFalse(store.contains(second)); + } + @Test public void testRemoveAttributeTimestamp() { Configuration config = Configuration.builder().timeRepresentation(TimeRepresentation.INTERVAL).build(); diff --git a/src/test/java/org/gephi/graph/impl/SerializationCompatibilityTest.java b/src/test/java/org/gephi/graph/impl/SerializationCompatibilityTest.java index 911782ea..2ef63ad7 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationCompatibilityTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationCompatibilityTest.java @@ -101,9 +101,9 @@ public void testFixtureDeserializes(String minor, String fixture) throws IOExcep // Contract 2: the current minor is byte-pinned // // Compares today's write path against the committed golden file. Reading a fixture back and re-serializing it - // would not work, because deserialization is not byte-idempotent here: GraphVersion counters and - // TimeIndexStore.countMap are restored from the stream and then incremented again as elements are re-inserted, and - // TextProperties width/height are dropped on read. Contract 1 covers the read path. + // would not work, because deserialization is not byte-idempotent here: GraphVersion counters are restored from the + // stream and then incremented again as elements are re-inserted, TextProperties width/height are dropped on read, + // and the time index is rebuilt from the elements rather than restored. Contract 1 covers the read path. @Test(dataProvider = "currentMinorFixtures") public void testCurrentMinorIsByteIdentical(String fixture) throws IOException { diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 92d2682f..9f3dc7a4 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -58,6 +58,7 @@ import java.util.Map; import java.util.Random; import java.util.Set; +import org.gephi.graph.api.Column; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.GraphModel; import org.gephi.graph.api.Origin; @@ -704,6 +705,11 @@ public void testTimeZone() throws IOException, ClassNotFoundException { Assert.assertEquals(ZoneId.of("+01:30"), l); } + /** + * The time store block is derived state, rebuilt from the elements as they are inserted. Its content is not + * written, read standalone it carries nothing, and it must consume exactly its own bytes: + * Serialization.deserialize(byte[]) throws if any are left. + */ @Test public void testTimestampStore() throws IOException, ClassNotFoundException { GraphModelImpl graphModel = new GraphModelImpl(); @@ -717,13 +723,16 @@ public void testTimestampStore() throws IOException, ClassNotFoundException { timestampStore.edgeIndexStore.add(3.0); timestampStore.edgeIndexStore.add(4.0); - Serialization ser = new Serialization(graphModel); - byte[] buf = ser.serialize(timestampStore); + Assert.assertFalse(timestampStore.isEmpty()); + byte[] buf = new Serialization(graphModel).serialize(timestampStore); - graphModel = new GraphModelImpl(); - ser = new Serialization(graphModel); - TimeStore l = (TimeStore) ser.deserialize(buf); - Assert.assertTrue(timestampStore.deepEquals(l)); + // The index is derived state: its content leaves no trace in the bytes and reading yields an empty store + GraphModelImpl empty = new GraphModelImpl(); + Assert.assertEquals(buf, new Serialization(empty).serialize(empty.store.timeStore)); + + GraphModelImpl readModel = new GraphModelImpl(); + TimeStore l = (TimeStore) new Serialization(readModel).deserialize(buf); + Assert.assertTrue(l.isEmpty()); } @Test @@ -740,13 +749,239 @@ public void testIntervalStore() throws IOException, ClassNotFoundException { timestampStore.edgeIndexStore.add(new Interval(2.0, 3.0)); timestampStore.edgeIndexStore.add(new Interval(2.0, 3.0)); - Serialization ser = new Serialization(graphModel); - byte[] buf = ser.serialize(timestampStore); + Assert.assertFalse(timestampStore.isEmpty()); + byte[] buf = new Serialization(graphModel).serialize(timestampStore); - graphModel = new GraphModelImpl(config); - ser = new Serialization(graphModel); - TimeStore l = (TimeStore) ser.deserialize(buf); - Assert.assertTrue(timestampStore.deepEquals(l)); + // The index is derived state: its content leaves no trace in the bytes and reading yields an empty store + GraphModelImpl empty = new GraphModelImpl(config); + Assert.assertEquals(buf, new Serialization(empty).serialize(empty.store.timeStore)); + + GraphModelImpl readModel = new GraphModelImpl(config); + TimeStore l = (TimeStore) new Serialization(readModel).deserialize(buf); + Assert.assertTrue(l.isEmpty()); + } + + @Test + public void testGraphStoreTimestampIndexCounts() throws IOException, ClassNotFoundException { + assertTimeIndexCountsSurviveRoundTrip(TimeRepresentation.TIMESTAMP); + } + + @Test + public void testGraphStoreIntervalIndexCounts() throws IOException, ClassNotFoundException { + assertTimeIndexCountsSurviveRoundTrip(TimeRepresentation.INTERVAL); + } + + @Test + public void testGraphStoreTimestampIndexRemoveAfterRoundTrip() throws IOException, ClassNotFoundException { + assertTimeIsRemovedFromIndexAfterRoundTrip(TimeRepresentation.TIMESTAMP); + } + + @Test + public void testGraphStoreIntervalIndexRemoveAfterRoundTrip() throws IOException, ClassNotFoundException { + assertTimeIsRemovedFromIndexAfterRoundTrip(TimeRepresentation.INTERVAL); + } + + @Test + public void testGraphStoreTimestampIndexIsRebuiltOnRead() throws IOException, ClassNotFoundException { + assertTimeIndexIsRebuiltOnRead(TimeRepresentation.TIMESTAMP); + } + + @Test + public void testGraphStoreIntervalIndexIsRebuiltOnRead() throws IOException, ClassNotFoundException { + assertTimeIndexIsRebuiltOnRead(TimeRepresentation.INTERVAL); + } + + /** + * Files written before #288 carry reference counts above the number of references, and time values nothing points + * at. Reading rebuilds the index from the elements, which drops both. The index is inflated directly here, since + * the write path no longer produces that state. + */ + private void assertTimeIndexIsRebuiltOnRead(TimeRepresentation timeRepresentation) throws IOException, ClassNotFoundException { + GraphModelImpl graphModel = new GraphModelImpl( + Configuration.builder().timeRepresentation(timeRepresentation).build()); + GraphStore graphStore = graphModel.store; + boolean interval = timeRepresentation.equals(TimeRepresentation.INTERVAL); + + Column column = graphStore.nodeTable + .addColumn("dynamic", interval ? IntervalIntegerMap.class : TimestampIntegerMap.class); + NodeImpl node1 = (NodeImpl) graphStore.factory.newNode("1"); + NodeImpl node2 = (NodeImpl) graphStore.factory.newNode("2"); + graphStore.addNode(node1); + graphStore.addNode(node2); + + setTimeAttribute(node1, column, timeRepresentation, 1.0, 10); + setTimeAttribute(node1, column, timeRepresentation, 2.0, 20); + setTimeAttribute(node1, column, timeRepresentation, 3.0, 30); + addTime(node2, timeRepresentation, 2.0); + + Object first = timeKey(timeRepresentation, 1.0); + Object shared = timeKey(timeRepresentation, 2.0); + Object last = timeKey(timeRepresentation, 3.0); + Object stranded = timeKey(timeRepresentation, 7.0); + + // Emulate a store written before #288: a count above the number of references, and a time no element holds + TimeIndexStore nodeIndexStore = graphStore.timeStore.nodeIndexStore; + nodeIndexStore.add(first); + nodeIndexStore.add(stranded); + Assert.assertEquals(nodeIndexStore.countMap[(Integer) nodeIndexStore.timeSortedMap.get(first)], 2); + Assert.assertTrue(nodeIndexStore.contains(stranded)); + + GraphStore read = roundTrip(graphStore, timeRepresentation); + TimeIndexStore readIndexStore = read.timeStore.nodeIndexStore; + + // Rebuilt from the elements: node 1 holds 1.0, 2.0 and 3.0, node 2 holds 2.0 + Assert.assertFalse(readIndexStore.contains(stranded)); + Assert.assertEquals(readIndexStore.size(), 3); + Assert.assertEquals(readIndexStore.countMap[(Integer) readIndexStore.timeSortedMap.get(first)], 1); + Assert.assertEquals(readIndexStore.countMap[(Integer) readIndexStore.timeSortedMap.get(shared)], 2); + Assert.assertEquals(readIndexStore.countMap[(Integer) readIndexStore.timeSortedMap.get(last)], 1); + + // The only reference to 3.0 is node 1's map, so dropping it frees the time + removeTimeAttribute(read.getNode("1"), read.nodeTable.getColumn("dynamic"), timeRepresentation, 3.0); + Assert.assertFalse(readIndexStore.contains(last)); + } + + private void assertTimeIndexCountsSurviveRoundTrip(TimeRepresentation timeRepresentation) throws IOException, ClassNotFoundException { + GraphStore graphStore = newTimeIndexGraphStore(timeRepresentation); + GraphStore read = roundTrip(graphStore, timeRepresentation); + + assertTimeIndexCountsEqual(graphStore.timeStore.nodeIndexStore, read.timeStore.nodeIndexStore); + assertTimeIndexCountsEqual(graphStore.timeStore.edgeIndexStore, read.timeStore.edgeIndexStore); + } + + private void assertTimeIsRemovedFromIndexAfterRoundTrip(TimeRepresentation timeRepresentation) throws IOException, ClassNotFoundException { + GraphStore read = roundTrip(newTimeIndexGraphStore(timeRepresentation), timeRepresentation); + TimeIndexStore nodeIndexStore = read.timeStore.nodeIndexStore; + + // Time 2.0 is referenced by two nodes and nothing else + Object timeKey = timeKey(timeRepresentation, 2.0); + Assert.assertTrue(nodeIndexStore.contains(timeKey)); + int timeIndex = (Integer) nodeIndexStore.timeSortedMap.get(timeKey); + + removeTime(read.getNode("1"), timeRepresentation, 2.0); + Assert.assertTrue(nodeIndexStore.contains(timeKey)); + removeTime(read.getNode("2"), timeRepresentation, 2.0); + + Assert.assertFalse(nodeIndexStore.contains(timeKey)); + Assert.assertTrue(nodeIndexStore.garbageQueue.contains(timeIndex)); + } + + /** + * Compares the time values and their reference counts. Slot numbering and the garbage queue are allocation state, + * not part of the contract: the index is rebuilt from the elements, so slots may be numbered differently. + */ + private static void assertTimeIndexCountsEqual(TimeIndexStore expected, TimeIndexStore actual) { + // With equal sizes, resolving every expected time value in actual makes this a full set comparison + Assert.assertEquals(actual.size(), expected.size()); + for (Object o : expected.timeSortedMap.entrySet()) { + Map.Entry entry = (Map.Entry) o; + Object timeKey = entry.getKey(); + Object actualTimeIndex = actual.timeSortedMap.get(timeKey); + Assert.assertNotNull(actualTimeIndex, "Missing time index for " + timeKey); + Assert.assertEquals(actual.countMap[(Integer) actualTimeIndex], expected.countMap[(Integer) entry + .getValue()], "Reference count for " + timeKey); + } + } + + /** + * Builds a store with element time sets, a dynamic column, times shared by several elements, a parallel edge, a + * freed time index and a dynamic time set twice. + */ + private static GraphStore newTimeIndexGraphStore(TimeRepresentation timeRepresentation) { + GraphModelImpl graphModel = new GraphModelImpl( + Configuration.builder().timeRepresentation(timeRepresentation).build()); + GraphStore graphStore = graphModel.store; + boolean interval = timeRepresentation.equals(TimeRepresentation.INTERVAL); + + Column column = graphStore.nodeTable + .addColumn("dynamic", interval ? IntervalIntegerMap.class : TimestampIntegerMap.class); + + NodeImpl node1 = (NodeImpl) graphStore.factory.newNode("1"); + NodeImpl node2 = (NodeImpl) graphStore.factory.newNode("2"); + NodeImpl node3 = (NodeImpl) graphStore.factory.newNode("3"); + graphStore.addNode(node1); + graphStore.addNode(node2); + graphStore.addNode(node3); + + EdgeImpl edge1 = (EdgeImpl) graphStore.factory.newEdge("1", node1, node2, 0, 1.0, true); + EdgeImpl edge2 = (EdgeImpl) graphStore.factory + .newEdge("2", node1, node2, graphStore.edgeTypeStore.addType("type"), 1.0, true); + graphStore.addEdge(edge1); + graphStore.addEdge(edge2); + + addTime(node1, timeRepresentation, 1.0); + addTime(node1, timeRepresentation, 2.0); + addTime(node2, timeRepresentation, 2.0); + addTime(edge1, timeRepresentation, 1.0); + addTime(edge2, timeRepresentation, 1.0); + addTime(edge2, timeRepresentation, 3.0); + + // Frees a time index, leaving the garbage queue non-empty + addTime(node3, timeRepresentation, 4.0); + removeTime(node3, timeRepresentation, 4.0); + + if (interval) { + IntervalIntegerMap map = new IntervalIntegerMap(); + map.put(new Interval(5.0, 5.0), 10); + map.put(new Interval(6.0, 6.0), 20); + node1.setAttribute(column, map); + } else { + TimestampIntegerMap map = new TimestampIntegerMap(); + map.put(5.0, 10); + map.put(6.0, 20); + node1.setAttribute(column, map); + } + + // 5.0 is already in the map and stays at one reference, 8.0 adds one + setTimeAttribute(node1, column, timeRepresentation, 5.0, 30); + setTimeAttribute(node1, column, timeRepresentation, 8.0, 40); + + return graphStore; + } + + private static GraphStore roundTrip(GraphStore graphStore, TimeRepresentation timeRepresentation) throws IOException, ClassNotFoundException { + Serialization ser = new Serialization(graphStore.graphModel); + byte[] buf = ser.serialize(graphStore); + + GraphModelImpl readModel = new GraphModelImpl( + Configuration.builder().timeRepresentation(timeRepresentation).build()); + return (GraphStore) new Serialization(readModel).deserialize(buf); + } + + private static Object timeKey(TimeRepresentation timeRepresentation, double time) { + return timeRepresentation.equals(TimeRepresentation.INTERVAL) ? new Interval(time, time) : (Double) time; + } + + private static void addTime(ElementImpl element, TimeRepresentation timeRepresentation, double time) { + if (timeRepresentation.equals(TimeRepresentation.INTERVAL)) { + element.addInterval(new Interval(time, time)); + } else { + element.addTimestamp(time); + } + } + + private static void setTimeAttribute(ElementImpl element, Column column, TimeRepresentation timeRepresentation, double time, int value) { + if (timeRepresentation.equals(TimeRepresentation.INTERVAL)) { + element.setAttribute(column, value, new Interval(time, time)); + } else { + element.setAttribute(column, value, time); + } + } + + private static void removeTimeAttribute(ElementImpl element, Column column, TimeRepresentation timeRepresentation, double time) { + if (timeRepresentation.equals(TimeRepresentation.INTERVAL)) { + element.removeAttribute(column, new Interval(time, time)); + } else { + element.removeAttribute(column, time); + } + } + + private static void removeTime(ElementImpl element, TimeRepresentation timeRepresentation, double time) { + if (timeRepresentation.equals(TimeRepresentation.INTERVAL)) { + element.removeInterval(new Interval(time, time)); + } else { + element.removeTimestamp(time); + } } @Test diff --git a/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java b/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java index 7b06fc75..049af2e3 100644 --- a/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java +++ b/src/test/java/org/gephi/graph/impl/TimestampIndexStoreTest.java @@ -451,6 +451,54 @@ public void testSetAttribute() { Assert.assertFalse(store.contains(2.0)); } + @Test + public void testSetAttributeTimestampCounts() { + GraphStore graphStore = new GraphStore(); + TimestampIndexStore store = (TimestampIndexStore) graphStore.timeStore.nodeIndexStore; + + Column col = graphStore.nodeTable.addColumn("col", TimestampStringMap.class); + NodeImpl nodeImpl = (NodeImpl) graphStore.factory.newNode("0"); + graphStore.addNode(nodeImpl); + + nodeImpl.setAttribute(col, "foo", 1.0); + nodeImpl.setAttribute(col, "bar", 2.0); + nodeImpl.setAttribute(col, "baz", 3.0); + // Overwriting an existing timestamp is not a new reference + nodeImpl.setAttribute(col, "qux", 1.0); + + Assert.assertEquals(store.size(), 3); + Assert.assertEquals(store.countMap[(Integer) store.timeSortedMap.get(1.0)], 1); + Assert.assertEquals(store.countMap[(Integer) store.timeSortedMap.get(2.0)], 1); + Assert.assertEquals(store.countMap[(Integer) store.timeSortedMap.get(3.0)], 1); + + // One reference each, so each removal frees its timestamp + nodeImpl.removeAttribute(col, 1.0); + Assert.assertFalse(store.contains(1.0)); + nodeImpl.removeAttribute(col); + Assert.assertEquals(store.size(), 0); + Assert.assertFalse(store.contains(2.0)); + Assert.assertFalse(store.contains(3.0)); + } + + @Test + public void testSetAttributeTimestampSharedWithTimeSet() { + GraphStore graphStore = new GraphStore(); + TimestampIndexStore store = (TimestampIndexStore) graphStore.timeStore.nodeIndexStore; + + Column col = graphStore.nodeTable.addColumn("col", TimestampStringMap.class); + NodeImpl nodeImpl = (NodeImpl) graphStore.factory.newNode("0"); + graphStore.addNode(nodeImpl); + + nodeImpl.addTimestamp(1.0); + nodeImpl.setAttribute(col, "foo", 1.0); + Assert.assertEquals(store.countMap[(Integer) store.timeSortedMap.get(1.0)], 2); + + nodeImpl.removeAttribute(col, 1.0); + Assert.assertTrue(store.contains(1.0)); + nodeImpl.removeTimestamp(1.0); + Assert.assertFalse(store.contains(1.0)); + } + @Test public void testRemoveAttributeTimestamp() { GraphStore graphStore = new GraphStore(); diff --git a/src/test/resources/serialization/0.8/graph-types-interval.graphstore b/src/test/resources/serialization/0.8/graph-types-interval.graphstore index 7f7842a6dfba80cb4a9bf43c599488d75e8eb100..eeea1d475ddc181eab0b7c6f359e04c80aeea3ef 100644 GIT binary patch delta 34 lcmaE8vCCq^VNrHgRvQKzhC37IiF3K8q^Cl-n}x*u*#N>(32*=a delta 73 zcmdmG@z7$!VNpd+&L@UOOsqhHmHmkklMyq=6C-9L7B(9O8x}5BM#ei6=ZTBErlhB` SvqKcZWSMx_Hoq42X9EE2%@UFT diff --git a/src/test/resources/serialization/0.8/graph-types-timestamp.graphstore b/src/test/resources/serialization/0.8/graph-types-timestamp.graphstore index 2bb6c50c3e69957e45e0ae00961f15ba448f019b..346853f5086eb36f0615987955bc694970bce0d6 100644 GIT binary patch delta 40 lcmZ2#{?cp%n-~|XDT56d-k!KXoYyraJryppxj@X66#(0w3B~{b delta 96 zcmaE9w$ywBo0uV|DU1CF1~71dPz(+nP`&_!wqaplW@NHquwmh5Wn`SpFD4_)ZpsAL V1yyas#J~tt!^Fe3*+k5g6#!Mq3cdgU From 836d81e1c620afe5d91f93278975abb1b8239f86 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 25 Aug 2026 13:52:15 +0200 Subject: [PATCH 264/271] Close graph-lock gaps in GraphModel.Serialization (#289) * Hold the graph read lock for the entire duration of serializeGraphStore Previously the only locking came incidentally from NodeStore/EdgeStore iterators, which release the lock between the node and edge loops and leave the configuration/columns/time store/views sections unprotected, so a serialized graph could observe a torn state under concurrent mutation. * Hold the graph write lock for the entire duration of deserializeGraphStore deserializeNode/deserializeEdge write directly into NodeStore/EdgeStore, bypassing GraphStore's own auto-locked addNode()/addEdge(), so deserialization had no lock coverage at all and could race with a concurrent reader on the same graph model. * Cleanup --- .../org/gephi/graph/impl/Serialization.java | 144 ++++---- .../gephi/graph/impl/SerializationTest.java | 338 ++++++++++++++++++ 2 files changed, 417 insertions(+), 65 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 2cd7917e..b0a72a4d 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -299,98 +299,112 @@ public GraphModelImpl deserializeGraphModelWithoutVersionPrefix(DataInput is, fl } public void serializeGraphStore(DataOutput out, GraphStore store) throws IOException { - // Configuration - serializeGraphStoreConfiguration(out); + // Hold the read lock for the whole method so the write is atomic with respect to + // concurrent structural mutation. + store.autoReadLock(); + try { + // Configuration + serializeGraphStoreConfiguration(out); - // GraphVersion - serialize(out, store.version); + // GraphVersion + serialize(out, store.version); - // Edge types - EdgeTypeStore edgeTypeStore = store.edgeTypeStore; - serialize(out, edgeTypeStore); + // Edge types + EdgeTypeStore edgeTypeStore = store.edgeTypeStore; + serialize(out, edgeTypeStore); - // Column - serialize(out, store.nodeTable); - serialize(out, store.edgeTable); + // Column + serialize(out, store.nodeTable); + serialize(out, store.edgeTable); - // Time store - serialize(out, store.timeStore); + // Time store + serialize(out, store.timeStore); - // Factory - serialize(out, store.factory); + // Factory + serialize(out, store.factory); - // Atts - serialize(out, store.attributes); + // Atts + serialize(out, store.attributes); - // TimeFormat - serialize(out, store.timeFormat); + // TimeFormat + serialize(out, store.timeFormat); - // Time zone - serialize(out, store.timeZone); + // Time zone + serialize(out, store.timeZone); - // Nodes + Edges - int nodesAndEdges = store.nodeStore.size() + store.edgeStore.size(); - serialize(out, nodesAndEdges); + // Nodes + Edges + int nodesAndEdges = store.nodeStore.size() + store.edgeStore.size(); + serialize(out, nodesAndEdges); - for (Node node : store.nodeStore) { - serialize(out, node); - } - for (Edge edge : store.edgeStore) { - serialize(out, edge); - } + for (Node node : store.nodeStore) { + serialize(out, node); + } + for (Edge edge : store.edgeStore) { + serialize(out, edge); + } - // Views - serialize(out, store.viewStore); + // Views + serialize(out, store.viewStore); + } finally { + store.autoReadUnlock(); + } } public GraphStore deserializeGraphStore(DataInput is) throws IOException, ClassNotFoundException { - if (!model.store.nodeStore.isEmpty()) { // TODO test other stores - throw new IOException("The store is not empty"); - } + GraphStore store = model.store; + // Hold the write lock for the whole method: deserialization mutates the store directly + store.autoWriteLock(); + try { + if (!store.nodeStore.isEmpty()) { // TODO test other stores + throw new IOException("The store is not empty"); + } - idMap.clear(); + idMap.clear(); - // Store Configuration - deserialize(is); - - // Graph Version - GraphVersion version = (GraphVersion) deserialize(is); - model.store.version.nodeVersion = version.nodeVersion; - model.store.version.edgeVersion = version.edgeVersion; + // Store Configuration + deserialize(is); - // Edge types - deserialize(is); + // Graph Version + GraphVersion version = (GraphVersion) deserialize(is); + store.version.nodeVersion = version.nodeVersion; + store.version.edgeVersion = version.edgeVersion; - // Columns - deserialize(is); - deserialize(is); + // Edge types + deserialize(is); - // Time store - deserialize(is); + // Columns + deserialize(is); + deserialize(is); - // Factory - deserialize(is); + // Time store + deserialize(is); - // Atts - GraphAttributesImpl attributes = (GraphAttributesImpl) deserialize(is); - model.store.attributes.setGraphAttributes(attributes); + // Factory + deserialize(is); - // TimeFormat - deserialize(is); + // Atts + GraphAttributesImpl attributes = (GraphAttributesImpl) deserialize(is); + store.attributes.setGraphAttributes(attributes); - // Time zone - deserialize(is); + // TimeFormat + deserialize(is); - // Nodes and edges - int nodesAndEdges = (Integer) deserialize(is); - for (int i = 0; i < nodesAndEdges; i++) { + // Time zone deserialize(is); - } - // ViewStore - deserialize(is); + // Nodes and edges + int nodesAndEdges = (Integer) deserialize(is); + for (int i = 0; i < nodesAndEdges; i++) { + deserialize(is); + } - return model.store; + // ViewStore + deserialize(is); + + return store; + } finally { + store.autoWriteUnlock(); + } } private void serializeNode(DataOutput out, NodeImpl node) throws IOException { diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 9f3dc7a4..06079a0b 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -40,7 +40,12 @@ import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; import it.unimi.dsi.fastutil.shorts.ShortArrayList; import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; import java.io.DataOutput; +import java.io.DataOutputStream; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -58,6 +63,10 @@ import java.util.Map; import java.util.Random; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.gephi.graph.api.Column; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.GraphModel; @@ -1565,4 +1574,333 @@ public void testSerializationTagsAreUnique() throws Exception { Assert.assertNull(previous, "Duplicate serialization tag " + value + " shared by " + previous + " and " + name); } } + + @Test(timeOut = 5000) + public void testSerializeGraphStoreHoldsReadLockForEntireDuration() throws Exception { + GraphModelImpl graphModel = new GraphModelImpl(); + GraphStore graphStore = graphModel.store; + NodeImpl[] nodes = GraphGenerator.generateSmallNodeList(); + graphStore.nodeStore.addAll(Arrays.asList(nodes)); + + CountDownLatch writeStarted = new CountDownLatch(1); + CountDownLatch proceed = new CountDownLatch(1); + BlockingDataOutput blockingOutput = new BlockingDataOutput(writeStarted, proceed); + + Serialization ser = new Serialization(graphModel); + AtomicReference writerError = new AtomicReference<>(); + Thread writer = new Thread(() -> { + try { + ser.serializeGraphStore(blockingOutput, graphStore); + } catch (Exception e) { + writerError.set(e); + } + }); + writer.start(); + + Assert.assertTrue(writeStarted.await(2, TimeUnit.SECONDS), "serialization should have started writing"); + + AtomicBoolean writeLockAcquired = new AtomicBoolean(false); + Thread locker = new Thread(() -> { + graphStore.writeLock(); + writeLockAcquired.set(true); + graphStore.writeUnlock(); + }); + locker.start(); + + // Give the locker thread a chance to attempt (and be blocked by) the write lock + Thread.sleep(300); + Assert.assertFalse(writeLockAcquired + .get(), "write lock must not be acquired while serializeGraphStore still holds the read lock"); + + proceed.countDown(); + writer.join(2000); + locker.join(2000); + + Assert.assertNull(writerError.get()); + Assert.assertTrue(writeLockAcquired.get(), "write lock should be acquired after serialization completes"); + } + + /** + * A DataOutput that blocks on its very first write call until released, so tests can deterministically assert what + * lock state holds while a serialization call is in progress. + */ + private static class BlockingDataOutput implements DataOutput { + + private final DataOutputStream delegate = new DataOutputStream(new ByteArrayOutputStream()); + private final CountDownLatch started; + private final CountDownLatch proceed; + private final AtomicBoolean first = new AtomicBoolean(true); + + private BlockingDataOutput(CountDownLatch started, CountDownLatch proceed) { + this.started = started; + this.proceed = proceed; + } + + private void beforeWrite() throws IOException { + if (first.compareAndSet(true, false)) { + started.countDown(); + try { + proceed.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } + } + } + + @Override + public void write(int b) throws IOException { + beforeWrite(); + delegate.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + beforeWrite(); + delegate.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + beforeWrite(); + delegate.write(b, off, len); + } + + @Override + public void writeBoolean(boolean v) throws IOException { + beforeWrite(); + delegate.writeBoolean(v); + } + + @Override + public void writeByte(int v) throws IOException { + beforeWrite(); + delegate.writeByte(v); + } + + @Override + public void writeShort(int v) throws IOException { + beforeWrite(); + delegate.writeShort(v); + } + + @Override + public void writeChar(int v) throws IOException { + beforeWrite(); + delegate.writeChar(v); + } + + @Override + public void writeInt(int v) throws IOException { + beforeWrite(); + delegate.writeInt(v); + } + + @Override + public void writeLong(long v) throws IOException { + beforeWrite(); + delegate.writeLong(v); + } + + @Override + public void writeFloat(float v) throws IOException { + beforeWrite(); + delegate.writeFloat(v); + } + + @Override + public void writeDouble(double v) throws IOException { + beforeWrite(); + delegate.writeDouble(v); + } + + @Override + public void writeBytes(String s) throws IOException { + beforeWrite(); + delegate.writeBytes(s); + } + + @Override + public void writeChars(String s) throws IOException { + beforeWrite(); + delegate.writeChars(s); + } + + @Override + public void writeUTF(String s) throws IOException { + beforeWrite(); + delegate.writeUTF(s); + } + } + + @Test(timeOut = 5000) + public void testDeserializeGraphStoreHoldsWriteLockForEntireDuration() throws Exception { + GraphModelImpl sourceModel = new GraphModelImpl(); + GraphStore sourceStore = sourceModel.store; + NodeImpl[] nodes = GraphGenerator.generateSmallNodeList(); + sourceStore.nodeStore.addAll(Arrays.asList(nodes)); + DataInputOutput dio = new DataInputOutput(); + new Serialization(sourceModel).serializeGraphStore(dio, sourceStore); + byte[] bytes = dio.toByteArray(); + + GraphModelImpl targetModel = new GraphModelImpl(); + GraphStore targetStore = targetModel.store; + + CountDownLatch readStarted = new CountDownLatch(1); + CountDownLatch proceed = new CountDownLatch(1); + BlockingDataInput blockingInput = new BlockingDataInput(bytes, readStarted, proceed); + + Serialization ser = new Serialization(targetModel); + AtomicReference readerError = new AtomicReference<>(); + Thread reader = new Thread(() -> { + try { + ser.deserializeGraphStore(blockingInput); + } catch (Exception e) { + readerError.set(e); + } + }); + reader.start(); + + Assert.assertTrue(readStarted.await(2, TimeUnit.SECONDS), "deserialization should have started reading"); + + AtomicBoolean readLockAcquired = new AtomicBoolean(false); + Thread locker = new Thread(() -> { + targetStore.readLock(); + readLockAcquired.set(true); + targetStore.readUnlock(); + }); + locker.start(); + + // Give the locker thread a chance to attempt (and be blocked by) the read lock + Thread.sleep(300); + Assert.assertFalse(readLockAcquired + .get(), "read lock must not be acquired while deserializeGraphStore still holds the write lock"); + + proceed.countDown(); + reader.join(2000); + locker.join(2000); + + Assert.assertNull(readerError.get()); + Assert.assertTrue(readLockAcquired.get(), "read lock should be acquired after deserialization completes"); + } + + /** + * A DataInput that blocks on its very first read call until released, so tests can deterministically assert what + * lock state holds while a deserialization call is in progress. + */ + private static class BlockingDataInput implements DataInput { + + private final DataInputStream delegate; + private final CountDownLatch started; + private final CountDownLatch proceed; + private final AtomicBoolean first = new AtomicBoolean(true); + + private BlockingDataInput(byte[] bytes, CountDownLatch started, CountDownLatch proceed) { + this.delegate = new DataInputStream(new ByteArrayInputStream(bytes)); + this.started = started; + this.proceed = proceed; + } + + private void beforeRead() throws IOException { + if (first.compareAndSet(true, false)) { + started.countDown(); + try { + proceed.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException(e); + } + } + } + + @Override + public void readFully(byte[] b) throws IOException { + beforeRead(); + delegate.readFully(b); + } + + @Override + public void readFully(byte[] b, int off, int len) throws IOException { + beforeRead(); + delegate.readFully(b, off, len); + } + + @Override + public int skipBytes(int n) throws IOException { + beforeRead(); + return delegate.skipBytes(n); + } + + @Override + public boolean readBoolean() throws IOException { + beforeRead(); + return delegate.readBoolean(); + } + + @Override + public byte readByte() throws IOException { + beforeRead(); + return delegate.readByte(); + } + + @Override + public int readUnsignedByte() throws IOException { + beforeRead(); + return delegate.readUnsignedByte(); + } + + @Override + public short readShort() throws IOException { + beforeRead(); + return delegate.readShort(); + } + + @Override + public int readUnsignedShort() throws IOException { + beforeRead(); + return delegate.readUnsignedShort(); + } + + @Override + public char readChar() throws IOException { + beforeRead(); + return delegate.readChar(); + } + + @Override + public int readInt() throws IOException { + beforeRead(); + return delegate.readInt(); + } + + @Override + public long readLong() throws IOException { + beforeRead(); + return delegate.readLong(); + } + + @Override + public float readFloat() throws IOException { + beforeRead(); + return delegate.readFloat(); + } + + @Override + public double readDouble() throws IOException { + beforeRead(); + return delegate.readDouble(); + } + + @Override + public String readLine() throws IOException { + beforeRead(); + return delegate.readLine(); + } + + @Override + public String readUTF() throws IOException { + beforeRead(); + return delegate.readUTF(); + } + } } From e069851fc252fa837572535215f27b0dc58496bf Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 25 Aug 2026 15:17:32 +0200 Subject: [PATCH 265/271] Fail fast on unsupported serialization format versions (#290) A file written by a newer graphstore than the one reading it currently fails deep inside deserialize()'s switch with a generic "Unknown serialization type tag" once it hits an unrecognized tag, after the store has already been locked/partially mutated. Check the version immediately after it's read, before touching any state, and raise a dedicated UnsupportedFormatVersionException (extends IOException, so no signature changes) that callers like Gephi can catch specifically to show a clean, localized message instead of a generic I/O error. Co-authored-by: Claude Sonnet 5 --- .../UnsupportedFormatVersionException.java | 56 +++++++++++++++++++ .../org/gephi/graph/impl/Serialization.java | 12 ++++ .../gephi/graph/impl/SerializationTest.java | 48 ++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 src/main/java/org/gephi/graph/api/UnsupportedFormatVersionException.java diff --git a/src/main/java/org/gephi/graph/api/UnsupportedFormatVersionException.java b/src/main/java/org/gephi/graph/api/UnsupportedFormatVersionException.java new file mode 100644 index 00000000..649172ce --- /dev/null +++ b/src/main/java/org/gephi/graph/api/UnsupportedFormatVersionException.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.api; + +import java.io.IOException; + +/** + * Thrown when reading a serialized graph model written by a newer, incompatible version of graphstore than the one + * doing the reading. + *

+ * Extends {@link IOException} so it is caught by existing code that only handles I/O errors, while callers that want to + * report a specific, localized message can catch this type directly and use {@link #getFileVersion()} and + * {@link #getMaxSupportedVersion()} instead of parsing the message. + */ +public class UnsupportedFormatVersionException extends IOException { + + private final float fileVersion; + private final float maxSupportedVersion; + + public UnsupportedFormatVersionException(float fileVersion, float maxSupportedVersion) { + super("Unsupported serialization format version: " + fileVersion + ". This file was written by a newer version of graphstore than this library supports (up to " + maxSupportedVersion + "). Please upgrade graphstore to read this file."); + this.fileVersion = fileVersion; + this.maxSupportedVersion = maxSupportedVersion; + } + + /** + * Returns the format version the file was written with. + * + * @return file format version + */ + public float getFileVersion() { + return fileVersion; + } + + /** + * Returns the highest format version this version of graphstore can read. + * + * @return max supported format version + */ + public float getMaxSupportedVersion() { + return maxSupportedVersion; + } +} diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index b0a72a4d..4f598b86 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -66,6 +66,7 @@ import org.gephi.graph.api.Origin; import org.gephi.graph.api.TimeFormat; import org.gephi.graph.api.TimeRepresentation; +import org.gephi.graph.api.UnsupportedFormatVersionException; import org.gephi.graph.api.types.IntervalBooleanMap; import org.gephi.graph.api.types.IntervalByteMap; import org.gephi.graph.api.types.IntervalCharMap; @@ -243,6 +244,7 @@ public void serializeGraphModel(DataOutput out, GraphModelImpl model) throws IOE public GraphModelImpl deserializeGraphModel(DataInput is) throws IOException, ClassNotFoundException { readVersion = (Float) deserialize(is); + checkVersionSupported(); ConfigurationImpl config = (ConfigurationImpl) deserialize(is); model = new GraphModelImpl(config.toConfiguration()); deserialize(is); @@ -252,12 +254,21 @@ public GraphModelImpl deserializeGraphModel(DataInput is) throws IOException, Cl public GraphModelImpl deserializeGraphModel(DataInput is, GraphModel graphModel) throws IOException, ClassNotFoundException { model = (GraphModelImpl) graphModel; readVersion = (Float) deserialize(is); + checkVersionSupported(); ConfigurationImpl config = (ConfigurationImpl) deserialize(is); verifyCompatibility(config, model.configuration); deserialize(is); return model; } + // Fails fast, before any store state is touched, instead of letting a later unrecognized type + // tag surface as a confusing "Unknown serialization type tag" mid-deserialization. + private void checkVersionSupported() throws UnsupportedFormatVersionException { + if (readVersion > VERSION) { + throw new UnsupportedFormatVersionException(readVersion, VERSION); + } + } + private void verifyCompatibility(ConfigurationImpl readConfig, ConfigurationImpl modelConfig) { // Time representation if (!readConfig.getTimeRepresentation().equals(modelConfig.getTimeRepresentation())) { @@ -292,6 +303,7 @@ private void verifyCompatibility(ConfigurationImpl readConfig, ConfigurationImpl public GraphModelImpl deserializeGraphModelWithoutVersionPrefix(DataInput is, float version) throws IOException, ClassNotFoundException { readVersion = version; + checkVersionSupported(); ConfigurationImpl config = (ConfigurationImpl) deserialize(is); model = new GraphModelImpl(config.toConfiguration()); deserialize(is); diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index 06079a0b..acf8256b 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -86,6 +86,7 @@ import org.gephi.graph.api.Edge; import org.gephi.graph.api.Interval; import org.gephi.graph.api.TimeRepresentation; +import org.gephi.graph.api.UnsupportedFormatVersionException; import org.gephi.graph.api.types.IntervalBooleanMap; import org.gephi.graph.api.types.IntervalByteMap; import org.gephi.graph.api.types.IntervalCharMap; @@ -1511,6 +1512,53 @@ public void serializeGraphModel(DataOutput out, GraphModelImpl model) throws IOE Assert.assertTrue(read.deepEquals(gm)); } + @Test + public void testDeserializeGraphModelRejectsNewerVersion() throws Exception { + GraphModelImpl gm = GraphGenerator.generateSmallGraphStore().graphModel; + Serialization ser = new Serialization(gm) { + @Override + public void serializeGraphModel(DataOutput out, GraphModelImpl model) throws IOException { + this.model = model; + serialize(out, VERSION + 1f); + serialize(out, model.configuration); + serialize(out, model.store); + } + }; + + DataInputOutput dio = new DataInputOutput(); + ser.serializeGraphModel(dio, gm); + byte[] bytes = dio.toByteArray(); + + UnsupportedFormatVersionException ex = Assert + .expectThrows(UnsupportedFormatVersionException.class, () -> new Serialization() + .deserializeGraphModel(dio.reset(bytes))); + Assert.assertEquals(ex.getFileVersion(), Serialization.VERSION + 1f); + Assert.assertEquals(ex.getMaxSupportedVersion(), Serialization.VERSION); + } + + @Test + public void testDeserializeGraphModelWithoutVersionPrefixRejectsNewerVersion() throws Exception { + GraphModelImpl gm = GraphGenerator.generateSmallGraphStore().graphModel; + Serialization ser = new Serialization(gm) { + @Override + public void serializeGraphModel(DataOutput out, GraphModelImpl model) throws IOException { + this.model = model; + serialize(out, model.configuration); + serialize(out, model.store); + } + }; + + DataInputOutput dio = new DataInputOutput(); + ser.serializeGraphModel(dio, gm); + byte[] bytes = dio.toByteArray(); + + UnsupportedFormatVersionException ex = Assert + .expectThrows(UnsupportedFormatVersionException.class, () -> new Serialization() + .deserializeGraphModelWithoutVersionPrefix(dio.reset(bytes), Serialization.VERSION + 1f)); + Assert.assertEquals(ex.getFileVersion(), Serialization.VERSION + 1f); + Assert.assertEquals(ex.getMaxSupportedVersion(), Serialization.VERSION); + } + @Test public void testReuseSerializationInstanceForDeserialization() throws Exception { GraphModelImpl gm1 = GraphGenerator.generateSmallGraphStore().graphModel; From ff8153fcc5a9f06e0abd804d91a3ca01efbdfe63 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 25 Aug 2026 15:59:19 +0200 Subject: [PATCH 266/271] Fix generateLargeGraphStore() building edges over detached nodes (#292) * Fix GraphGenerator.generateLargeGraphStore() building edges over detached nodes generateLargeEdgeList() creates edges against its own independently- sized, throwaway NodeStore rather than the real one - so edge.source/ target only lined up with generateLargeGraphStore()'s actual inserted nodes by numeric coincidence (matching storeId, different objects). removeNode()'s cascade-edge-removal walks the real node's own adjacency links, which were never wired to these edges, so removing a referenced node silently left a dangling edge behind instead of cascading. That only surfaced once something both spanned multiple storage blocks and had elements removed afterward - traced back to plain, pre-existing sequential serialization code, not anything specific to threading. Root cause confirmed directly: graphStore.nodeStore.get(edge.source. storeId) != edge.source for every edge before this fix. Fix: build edges via generateEdgeList(graphStore.nodeStore, ...) so they reference the real nodes. generateLargeNodeList()/ generateLargeEdgeList() are left untouched since other tests use them independently; generateLargeGraphStore() was unused before this session's tests, so nothing depended on the old behavior. * Move the regression test to a dedicated GraphGeneratorTest The fix touches GraphGenerator, not Serialization, so the test should exercise that directly rather than proving it indirectly through a full serialize/deserialize round-trip. Asserts the actual invariant that broke: every edge's source/target is the same object registered in the store's own nodeStore, not just one with a matching storeId. Confirmed red on the pre-fix generator, green after. * Remove explanatory comment from generateLargeGraphStore() --- .../org/gephi/graph/impl/GraphGenerator.java | 2 +- .../gephi/graph/impl/GraphGeneratorTest.java | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/gephi/graph/impl/GraphGeneratorTest.java diff --git a/src/test/java/org/gephi/graph/impl/GraphGenerator.java b/src/test/java/org/gephi/graph/impl/GraphGenerator.java index 31ddf79e..07f4947c 100644 --- a/src/test/java/org/gephi/graph/impl/GraphGenerator.java +++ b/src/test/java/org/gephi/graph/impl/GraphGenerator.java @@ -551,7 +551,7 @@ public static GraphStore generateLargeGraphStore() { NodeImpl[] nodes = generateLargeNodeList(); graphStore.addAllNodes(Arrays.asList(nodes)); - EdgeImpl[] edges = generateLargeEdgeList(); + EdgeImpl[] edges = generateEdgeList(graphStore.nodeStore, GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE * 3 + (int) (GraphStoreConfiguration.EDGESTORE_BLOCK_SIZE / 3.0), 0, true, true, false); graphStore.addAllEdges(Arrays.asList(edges)); return graphStore; } diff --git a/src/test/java/org/gephi/graph/impl/GraphGeneratorTest.java b/src/test/java/org/gephi/graph/impl/GraphGeneratorTest.java new file mode 100644 index 00000000..49bd4731 --- /dev/null +++ b/src/test/java/org/gephi/graph/impl/GraphGeneratorTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2013 Gephi Consortium + * + * Licensed 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.gephi.graph.impl; + +import org.gephi.graph.api.Edge; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class GraphGeneratorTest { + + @Test + public void testGenerateLargeGraphStoreEdgesReferenceRealNodes() { + GraphStore graphStore = GraphGenerator.generateLargeGraphStore(); + + // Edges must reference the same node objects registered in the store's own nodeStore, not just + // objects that happen to carry a matching storeId - otherwise removeNode()'s cascade-edge-removal + // (which walks the real node's own adjacency links) silently fails to find and remove them. + for (Edge edge : graphStore.edgeStore) { + EdgeImpl e = (EdgeImpl) edge; + Assert.assertSame(graphStore.nodeStore.get(e.source.storeId), e.source); + Assert.assertSame(graphStore.nodeStore.get(e.target.storeId), e.target); + } + } +} From 03fed35f87ee33cdf92ee29b94d17cc5e256a158 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Tue, 25 Aug 2026 16:59:58 +0200 Subject: [PATCH 267/271] Parallelize node/edge encoding in serializeGraphStore (#291) * Use direct typed writes for node/edge serialization serializeNode/serializeEdge and the main write loop routed every scalar field (storeId, edge type, weight, directed flag, properties) through the generic ~110-branch serialize(DataOutput, Object) dispatcher. Call the type-specific writers directly instead - byte-identical by construction, and removes a per-element dispatch cost that would otherwise be multiplied across worker threads once serialization is parallelized. * Parallelize node/edge encoding in serializeGraphStore NodeStore/EdgeStore already have a lock-free, block-boundary-aware Spliterator (backing parallelStream()) that splits at storage block boundaries and skips garbage slots. Use it to fan node/edge encoding out across a per-call thread pool whenever a store spans more than one block, and drain results back to the output stream through a bounded in-flight window (not invokeAll) so peak memory stays bounded instead of materializing the whole payload at once. Below the single-block threshold - which trySplit() reports on its own - encoding stays on the calling thread with the exact same code path, so small graphs are unaffected. Output bytes are unchanged either way: no shared mutable state exists on the write path, so concatenating independently-encoded chunks in original block order reproduces today's exact serialization format. --- .../org/gephi/graph/impl/Serialization.java | 245 +++++++++++++++--- .../gephi/graph/impl/SerializationTest.java | 113 ++++++++ 2 files changed, 321 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/Serialization.java b/src/main/java/org/gephi/graph/impl/Serialization.java index 4f598b86..1a4fec99 100644 --- a/src/main/java/org/gephi/graph/impl/Serialization.java +++ b/src/main/java/org/gephi/graph/impl/Serialization.java @@ -48,15 +48,24 @@ import java.io.DataOutput; import java.io.EOFException; import java.io.IOException; +import java.io.UncheckedIOException; import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; import java.time.Instant; import java.time.ZoneId; +import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Spliterator; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.Edge; import org.gephi.graph.api.Estimator; @@ -348,12 +357,7 @@ public void serializeGraphStore(DataOutput out, GraphStore store) throws IOExcep int nodesAndEdges = store.nodeStore.size() + store.edgeStore.size(); serialize(out, nodesAndEdges); - for (Node node : store.nodeStore) { - serialize(out, node); - } - for (Edge edge : store.edgeStore) { - serialize(out, edge); - } + serializeNodesAndEdges(out, store); // Views serialize(out, store.viewStore); @@ -362,6 +366,160 @@ public void serializeGraphStore(DataOutput out, GraphStore store) throws IOExcep } } + /** + * Writes every node then every edge to out. If either store spans more than one internal storage + * block, encoding is fanned out across worker threads (one thread per block, or groups of blocks); the encoded + * bytes are still written to out from this thread, in the same node-then-edge, block order as the + * plain sequential loop would produce. + */ + private void serializeNodesAndEdges(DataOutput out, GraphStore store) throws IOException { + List> nodeChunks = splitIntoBlockChunks(store.nodeStore.spliterator()); + List> edgeChunks = splitIntoBlockChunks(store.edgeStore.spliterator()); + + if (nodeChunks.size() > 1 || edgeChunks.size() > 1) { + serializeChunksInParallel(out, nodeChunks, edgeChunks); + } else { + serializeChunksSequentially(out, nodeChunks, edgeChunks); + } + } + + /** + * Recursively decomposes a spliterator into the ordered list of pieces it bottoms out to (one per internal storage + * block, since {@code trySplit()} on the node/edge store spliterators only splits at block boundaries and returns + * null once a piece is a single block). Returns a single-element list, containing root + * unchanged, when there's nothing to split. + */ + static List> splitIntoBlockChunks(Spliterator root) { + List> chunks = new ArrayList<>(); + collectBlockChunks(root, chunks); + return chunks; + } + + private static void collectBlockChunks(Spliterator spliterator, List> chunks) { + // trySplit() returns the first half and mutates its receiver into the second half, so the + // recursion order below (left, then the mutated spliterator) preserves original element order. + Spliterator left = spliterator.trySplit(); + if (left == null) { + chunks.add(spliterator); + return; + } + collectBlockChunks(left, chunks); + collectBlockChunks(spliterator, chunks); + } + + void serializeChunksSequentially(DataOutput out, List> nodeChunks, List> edgeChunks) throws IOException { + try { + for (Spliterator chunk : nodeChunks) { + chunk.forEachRemaining(node -> writeNodeUnchecked(out, (NodeImpl) node)); + } + for (Spliterator chunk : edgeChunks) { + chunk.forEachRemaining(edge -> writeEdgeUnchecked(out, (EdgeImpl) edge)); + } + } catch (UncheckedIOException e) { + throw e.getCause(); + } + } + + private void writeNodeUnchecked(DataOutput out, NodeImpl node) { + try { + out.write(NODE); + serializeNode(out, node); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void writeEdgeUnchecked(DataOutput out, EdgeImpl edge) { + try { + out.write(EDGE); + serializeEdge(out, edge); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + void serializeChunksInParallel(DataOutput out, List> nodeChunks, List> edgeChunks) throws IOException { + int threadCount = Math.max(1, Runtime.getRuntime().availableProcessors() - 1); + ExecutorService executor = Executors.newFixedThreadPool(threadCount, r -> { + Thread t = new Thread(r, "graphstore-serialize"); + t.setDaemon(true); + return t; + }); + try { + List> tasks = new ArrayList<>(nodeChunks.size() + edgeChunks.size()); + for (Spliterator chunk : nodeChunks) { + tasks.add(() -> encodeNodeChunk(chunk)); + } + for (Spliterator chunk : edgeChunks) { + tasks.add(() -> encodeEdgeChunk(chunk)); + } + drainInOrder(out, executor, tasks, threadCount * 2); + } finally { + executor.shutdownNow(); + } + } + + // this/serializeNode/serializeEdge touch no instance state (idMap/model/readVersion are + // deserialize-only), so calling them concurrently from multiple worker threads on the same + // Serialization instance is safe as long as each call writes to its own private buffer. + private DataInputOutput encodeNodeChunk(Spliterator chunk) throws IOException { + DataInputOutput buffer = new DataInputOutput(); + try { + chunk.forEachRemaining(node -> writeNodeUnchecked(buffer, (NodeImpl) node)); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + return buffer; + } + + private DataInputOutput encodeEdgeChunk(Spliterator chunk) throws IOException { + DataInputOutput buffer = new DataInputOutput(); + try { + chunk.forEachRemaining(edge -> writeEdgeUnchecked(buffer, (EdgeImpl) edge)); + } catch (UncheckedIOException e) { + throw e.getCause(); + } + return buffer; + } + + private void drainInOrder(DataOutput out, ExecutorService executor, List> tasks, int maxInFlight) throws IOException { + ArrayDeque> inFlight = new ArrayDeque<>(); + int nextToSubmit = 0; + try { + while (nextToSubmit < Math.min(maxInFlight, tasks.size())) { + inFlight.add(executor.submit(tasks.get(nextToSubmit++))); + } + while (!inFlight.isEmpty()) { + DataInputOutput buffer = inFlight.pollFirst().get(); + out.write(buffer.getBuf(), 0, buffer.getPos()); + if (nextToSubmit < tasks.size()) { + inFlight.add(executor.submit(tasks.get(nextToSubmit++))); + } + } + } catch (ExecutionException e) { + cancelAll(inFlight); + Throwable cause = e.getCause(); + if (cause instanceof IOException) { + throw (IOException) cause; + } else if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else if (cause instanceof Error) { + throw (Error) cause; + } + throw new IOException(cause); + } catch (InterruptedException e) { + cancelAll(inFlight); + Thread.currentThread().interrupt(); + throw new IOException(e); + } + } + + private static void cancelAll(ArrayDeque> inFlight) { + for (Future future : inFlight) { + future.cancel(true); + } + } + public GraphStore deserializeGraphStore(DataInput is) throws IOException, ClassNotFoundException { GraphStore store = model.store; // Hold the write lock for the whole method: deserialization mutates the store directly @@ -421,24 +579,34 @@ public GraphStore deserializeGraphStore(DataInput is) throws IOException, ClassN private void serializeNode(DataOutput out, NodeImpl node) throws IOException { serialize(out, node.getId()); - serialize(out, node.storeId); + writeInteger(out, node.storeId); serialize(out, node.attributes.attributes); - serialize(out, node.properties); + if (node.properties != null) { + out.write(NODE_PROPERTIES); + serializeNodeProperties(out, node.properties); + } else { + out.write(NULL); + } } private void serializeEdge(DataOutput out, EdgeImpl edge) throws IOException { serialize(out, edge.getId()); - serialize(out, edge.source.storeId); - serialize(out, edge.target.storeId); - serialize(out, edge.type); + writeInteger(out, edge.source.storeId); + writeInteger(out, edge.target.storeId); + writeInteger(out, edge.type); if (edge.graphStore != null && edge.hasDynamicWeight()) { - serialize(out, edge.getWeight()); + writeDouble(out, edge.getWeight()); } else { - serialize(out, GraphStoreConfiguration.DEFAULT_EDGE_WEIGHT); + writeDouble(out, GraphStoreConfiguration.DEFAULT_EDGE_WEIGHT); } - serialize(out, edge.isDirected()); + writeBoolean(out, edge.isDirected()); serialize(out, edge.attributes.attributes); - serialize(out, edge.properties); + if (edge.properties != null) { + out.write(EDGE_PROPERTIES); + serializeEdgeProperties(out, edge.properties); + } else { + out.write(NULL); + } } private NodeImpl deserializeNode(DataInput is) throws IOException, ClassNotFoundException { @@ -1388,35 +1556,15 @@ protected void serialize(final DataOutput out, final Object obj) throws IOExcept out.write(NULL); } else if (clazz == Boolean.class) { - if (((Boolean) obj)) { - out.write(BOOLEAN_TRUE); - } else { - out.write(BOOLEAN_FALSE); + writeBoolean(out, (Boolean) obj); - } } else if (clazz == Integer.class) { final int val = (Integer) obj; writeInteger(out, val); } else if (clazz == Double.class) { - double v = (Double) obj; - if (v == -1d) { - out.write(DOUBLE_MINUS_1); - } else if (v == 0d) { - out.write(DOUBLE_0); - } else if (v == 1d) { - out.write(DOUBLE_1); - } else if (v >= 0 && v <= 255 && (int) v == v) { - out.write(DOUBLE_255); - out.write((int) v); - } else if (v >= Short.MIN_VALUE && v <= Short.MAX_VALUE && (short) v == v) { - out.write(DOUBLE_SHORT); - out.writeShort((int) v); - } else { - out.write(DOUBLE_FULL); - out.writeDouble(v); + writeDouble(out, (Double) obj); - } } else if (clazz == Float.class) { float v = (Float) obj; if (v == -1f) { @@ -1795,6 +1943,29 @@ private void writeIntArray(DataOutput da, int[] obj) throws IOException { } + private void writeBoolean(DataOutput da, final boolean val) throws IOException { + da.write(val ? BOOLEAN_TRUE : BOOLEAN_FALSE); + } + + private void writeDouble(DataOutput da, final double v) throws IOException { + if (v == -1d) { + da.write(DOUBLE_MINUS_1); + } else if (v == 0d) { + da.write(DOUBLE_0); + } else if (v == 1d) { + da.write(DOUBLE_1); + } else if (v >= 0 && v <= 255 && (int) v == v) { + da.write(DOUBLE_255); + da.write((int) v); + } else if (v >= Short.MIN_VALUE && v <= Short.MAX_VALUE && (short) v == v) { + da.write(DOUBLE_SHORT); + da.writeShort((int) v); + } else { + da.write(DOUBLE_FULL); + da.writeDouble(v); + } + } + private void writeInteger(DataOutput da, final int val) throws IOException { if (val == -1) { da.write(INTEGER_MINUS_1); diff --git a/src/test/java/org/gephi/graph/impl/SerializationTest.java b/src/test/java/org/gephi/graph/impl/SerializationTest.java index acf8256b..6697ed39 100644 --- a/src/test/java/org/gephi/graph/impl/SerializationTest.java +++ b/src/test/java/org/gephi/graph/impl/SerializationTest.java @@ -63,13 +63,16 @@ import java.util.Map; import java.util.Random; import java.util.Set; +import java.util.Spliterator; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import org.gephi.graph.api.Column; import org.gephi.graph.api.Configuration; import org.gephi.graph.api.GraphModel; +import org.gephi.graph.api.Node; import org.gephi.graph.api.Origin; import org.gephi.graph.api.Estimator; import org.gephi.graph.api.TimeFormat; @@ -1951,4 +1954,114 @@ public String readUTF() throws IOException { return delegate.readUTF(); } } + + private GraphStore buildMultiBlockGraphWithGarbage() { + GraphStore graphStore = GraphGenerator.generateLargeGraphStore(); + + // Delete a scattered subset of nodes/edges so garbage (removed) slots fall across multiple + // blocks, exercising the same "skip garbage slots" behavior in the block-splitting spliterator + // that the plain sequential iterators already rely on. + NodeImpl[] nodes = graphStore.nodeStore.toArray(); + for (int i = 0; i < nodes.length; i += 11) { + graphStore.removeNode(nodes[i]); + } + EdgeImpl[] edges = graphStore.edgeStore.toArray(); + for (int i = 0; i < edges.length; i += 5) { + graphStore.removeEdge(edges[i]); + } + return graphStore; + } + + @Test + public void testParallelAndSequentialNodeEdgeEncodingProduceIdenticalBytes() throws Exception { + GraphStore graphStore = buildMultiBlockGraphWithGarbage(); + Serialization ser = new Serialization(); + + List> nodeChunks = Serialization.splitIntoBlockChunks(graphStore.nodeStore.spliterator()); + List> edgeChunks = Serialization.splitIntoBlockChunks(graphStore.edgeStore.spliterator()); + Assert.assertTrue(nodeChunks.size() > 1 || edgeChunks + .size() > 1, "test graph should span more than one block to be meaningful"); + + DataInputOutput sequentialOut = new DataInputOutput(); + ser.serializeChunksSequentially(sequentialOut, Serialization.splitIntoBlockChunks(graphStore.nodeStore + .spliterator()), Serialization.splitIntoBlockChunks(graphStore.edgeStore.spliterator())); + + DataInputOutput parallelOut = new DataInputOutput(); + ser.serializeChunksInParallel(parallelOut, Serialization.splitIntoBlockChunks(graphStore.nodeStore + .spliterator()), Serialization.splitIntoBlockChunks(graphStore.edgeStore.spliterator())); + + Assert.assertEquals(Arrays.copyOf(parallelOut.getBuf(), parallelOut.getPos()), Arrays + .copyOf(sequentialOut.getBuf(), sequentialOut.getPos())); + } + + @Test + public void testSerializeAndDeserializeMultiBlockGraphRoundTrips() throws Exception { + GraphStore graphStore = GraphGenerator.generateLargeGraphStore(); + Assert.assertTrue(Serialization.splitIntoBlockChunks(graphStore.nodeStore.spliterator()) + .size() > 1, "test graph should span more than one block to exercise the parallel path"); + + GraphModelImpl graphModel = new GraphModelImpl(); + Serialization ser = new Serialization(graphModel); + DataInputOutput dio = new DataInputOutput(); + ser.serializeGraphStore(dio, graphStore); + byte[] bytes = dio.toByteArray(); + + GraphModelImpl graphModel2 = new GraphModelImpl(); + Serialization ser2 = new Serialization(graphModel2); + GraphStore deserialized = ser2.deserializeGraphStore(dio.reset(bytes)); + + Assert.assertTrue(graphStore.nodeStore.deepEquals(deserialized.nodeStore)); + Assert.assertTrue(graphStore.edgeStore.deepEquals(deserialized.edgeStore)); + } + + @Test(timeOut = 5000) + public void testSerializeChunksInParallelPropagatesExceptionAndLeavesNoThreads() throws Exception { + Serialization ser = new Serialization(); + List> nodeChunks = new ArrayList<>(); + nodeChunks.add(new ThrowingSpliterator<>()); + nodeChunks.add(new ThrowingSpliterator<>()); + List> edgeChunks = new ArrayList<>(); + + Assert.expectThrows(RuntimeException.class, () -> ser + .serializeChunksInParallel(new DataInputOutput(), nodeChunks, edgeChunks)); + + long deadline = System.currentTimeMillis() + 2000; + long remaining; + do { + remaining = countGraphstoreSerializeThreads(); + if (remaining == 0) { + break; + } + Thread.sleep(50); + } while (System.currentTimeMillis() < deadline); + Assert.assertEquals(remaining, 0, "no graphstore-serialize threads should remain after a failed parallel encode"); + } + + private static long countGraphstoreSerializeThreads() { + return Thread.getAllStackTraces().keySet().stream().filter(t -> "graphstore-serialize".equals(t.getName())) + .count(); + } + + private static final class ThrowingSpliterator implements Spliterator { + + @Override + public boolean tryAdvance(Consumer action) { + throw new RuntimeException("boom"); + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return 1; + } + + @Override + public int characteristics() { + return 0; + } + } } From 80a3035913320b27fb84319c1322926f93f150e5 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 26 Aug 2026 09:55:51 +0200 Subject: [PATCH 268/271] Update version to 0.8.7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f528f70a..911a41a4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.7-SNAPSHOT + 0.8.7 jar GraphStore From 7368f437d8b99733f37238cee1ef2189e7cccace Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 26 Aug 2026 10:04:30 +0200 Subject: [PATCH 269/271] Set version to 0.8.8-SNAPSHOT --- README.md | 7 +++---- pom.xml | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8a96095a..a979958d 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,7 @@ GraphStore is an in-memory graph structure implementation written in Java. It's Stable releases can be found on [Maven central](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.gephi%22%20AND%20a%3A%22graphstore%22). -Development builds can be found on [Sonatype's Snapshot Repository](https://oss.sonatype.org/content/repositories/snapshots/org/gephi/graphstore/). - +Development builds can be found on Maven's snapshot repository. ## Documentation API Documentation is available [here](https://www.javadoc.io/doc/org.gephi/graphstore/latest/index.html). @@ -41,14 +40,14 @@ Follow [this QuickStart](https://github.com/gephi/graphstore/wiki/Quick-Start) t org.gephi graphstore - 0.8.6 + 0.8.7 ``` ### From a Gradle project ``` -compile 'org.gephi:graphstore:0.8.6' +compile 'org.gephi:graphstore:0.8.7' ``` ## Dependencies diff --git a/pom.xml b/pom.xml index 911a41a4..84918177 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.gephi graphstore - 0.8.7 + 0.8.8-SNAPSHOT jar GraphStore From cef773fa00b1351e78ed35dbb27955d74ea65e42 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Thu, 27 Aug 2026 14:32:07 +0200 Subject: [PATCH 270/271] Fix unquoted array string values being truncated at parenthesis (#293) FormattingAndParsingUtils.parseValue() stopped unquoted value parsing at ')' as well as ']', a rule intended for interval bounds like "(1,2)". ArraysParser reused the same method for plain string/array elements, so a literal ')' inside an unquoted array element (e.g. liststring value "[Foo,Bar(Foo)]") was misread as the end of the value and silently dropped. Arrays are only ever delimited by '[', ']' and ',', so '(' and ')' now have no structural meaning there and are kept as part of the value. Fixes gephi/gephi#2989 --- .../org/gephi/graph/impl/ArraysParser.java | 6 +---- .../graph/impl/FormattingAndParsingUtils.java | 24 +++++++++++++++++-- .../gephi/graph/impl/ArraysParserTest.java | 9 +++++++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/gephi/graph/impl/ArraysParser.java b/src/main/java/org/gephi/graph/impl/ArraysParser.java index bdbf19be..bfe1e8ec 100644 --- a/src/main/java/org/gephi/graph/impl/ArraysParser.java +++ b/src/main/java/org/gephi/graph/impl/ArraysParser.java @@ -17,9 +17,7 @@ import static org.gephi.graph.impl.FormattingAndParsingUtils.COMMA; import static org.gephi.graph.impl.FormattingAndParsingUtils.EMPTY_VALUE; -import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.LEFT_BOUND_SQUARE_BRACKET; -import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_BRACKET; import static org.gephi.graph.impl.FormattingAndParsingUtils.RIGHT_BOUND_SQUARE_BRACKET; import java.io.IOException; @@ -83,8 +81,6 @@ public static T[] parseArray(Class arrayTypeClass, String input) throws c = (char) r; switch (c) { case RIGHT_BOUND_SQUARE_BRACKET: - case RIGHT_BOUND_BRACKET: - case LEFT_BOUND_BRACKET: case LEFT_BOUND_SQUARE_BRACKET: case ' ': case '\t': @@ -101,7 +97,7 @@ public static T[] parseArray(Class arrayTypeClass, String input) throws default: reader.skip(-1);// Go backwards 1 position, for reading // start of value - String value = FormattingAndParsingUtils.parseValue(reader); + String value = FormattingAndParsingUtils.parseValue(reader, false); if (value.equals("null")) { value = null;// Special null value only when not in // literal parsing mode diff --git a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java index 28186904..4c434f5b 100644 --- a/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java +++ b/src/main/java/org/gephi/graph/impl/FormattingAndParsingUtils.java @@ -126,20 +126,40 @@ protected static String parseLiteral(StringReader reader, char quote) throws IOE } /** - * Parses a value until end is detected either by a comma or a bounds closing character. + * Parses a value until end is detected either by a comma or a bounds closing character. Both {@code )} and + * {@code ]} are treated as a closing bound, since this is used for parsing interval and timestamp bounds which can + * be opened/closed with either bracket type. * * @param reader Input reader * @return Parsed value * @throws IOException Unexpected read error */ protected static String parseValue(StringReader reader) throws IOException { + return parseValue(reader, true); + } + + /** + * Parses a value until end is detected either by a comma or a bounds closing character. + * + * @param reader Input reader + * @param roundBracketIsClosingBound Whether {@code )} should be treated as a closing bound character in addition to + * {@code ]}. This should be disabled for grammars, such as plain arrays, where {@code (} and {@code )} have + * no structural meaning and can be part of an unquoted value. + * @return Parsed value + * @throws IOException Unexpected read error + */ + protected static String parseValue(StringReader reader, boolean roundBracketIsClosingBound) throws IOException { StringBuilder sb = new StringBuilder(); int r; char c; while ((r = reader.read()) != -1) { c = (char) r; + if (roundBracketIsClosingBound && c == RIGHT_BOUND_BRACKET) { + reader.skip(-1);// Go backwards 1 position, for detecting end + // of bounds + return sb.toString().trim(); + } switch (c) { - case RIGHT_BOUND_BRACKET: case RIGHT_BOUND_SQUARE_BRACKET: reader.skip(-1);// Go backwards 1 position, for detecting // end of bounds diff --git a/src/test/java/org/gephi/graph/impl/ArraysParserTest.java b/src/test/java/org/gephi/graph/impl/ArraysParserTest.java index af57c936..d4c49493 100644 --- a/src/test/java/org/gephi/graph/impl/ArraysParserTest.java +++ b/src/test/java/org/gephi/graph/impl/ArraysParserTest.java @@ -46,6 +46,15 @@ public void testParseString() { Assert.assertEquals(new String[] { "-1", " value", "value2" }, a1); } + @Test + public void testParseStringWithParenthesis() { + String[] a1 = ArraysParser.parseArray(String[].class, "[Foo,Bar(Foo)]"); + String[] a2 = ArraysParser.parseArray(String[].class, "[(Foo), Bar(Foo), Baz)(]"); + + Assert.assertEquals(new String[] { "Foo", "Bar(Foo)" }, a1); + Assert.assertEquals(new String[] { "(Foo)", "Bar(Foo)", "Baz)(" }, a2); + } + @Test public void testParseCharacter() { Character[] a1 = ArraysParser.parseArray(Character[].class, "[a, b, c, 2, 9]"); From 890e9e75fc08b93d2b253d479863c1ebe373078f Mon Sep 17 00:00:00 2001 From: Matt Artz <56270051+MattArtzAnthro@users.noreply.github.com> Date: Fri, 28 Aug 2026 09:40:24 -0400 Subject: [PATCH 271/271] Add timed acquisition and queue diagnostics to GraphLock GraphLock exposed only unbounded, non-interruptible lock() calls, so a caller that cannot afford to wait indefinitely had no option in the public API. This adds tryReadLock and tryWriteLock with a timeout, plus getReadLockCount, isWriteLocked, and getQueueLength for monitoring, each delegating to the underlying ReentrantReadWriteLock. New methods are default methods on the interface so existing implementations keep compiling. Javadoc on readLock now states the consequence of holding a read lock across a cross-thread wait or abandoning an auto-locking iterator. Fixes #282. --- .../java/org/gephi/graph/api/GraphLock.java | 77 +++++++++ .../org/gephi/graph/impl/GraphLockImpl.java | 30 ++++ .../gephi/graph/impl/GraphLockImplTest.java | 162 ++++++++++++++++++ 3 files changed, 269 insertions(+) diff --git a/src/main/java/org/gephi/graph/api/GraphLock.java b/src/main/java/org/gephi/graph/api/GraphLock.java index e244e2d3..5ae4d08c 100644 --- a/src/main/java/org/gephi/graph/api/GraphLock.java +++ b/src/main/java/org/gephi/graph/api/GraphLock.java @@ -15,6 +15,8 @@ */ package org.gephi.graph.api; +import java.util.concurrent.TimeUnit; + /** * Wrapper around ReentrantReadWriteLock that controls multi-thread access to the graph structure. */ @@ -23,6 +25,12 @@ public interface GraphLock { /** * Acquires the read lock. Acquires the read lock if the write lock is not held by another thread and returns * immediately. + *

+ * This call waits without bound. A read hold blocks every writer, and once a writer is waiting, new readers wait + * behind it as well, so a read hold that is never released stalls all graph operations. Do not hold the read lock + * across a wait on another thread, and do not abandon an auto-locking iterator (see {@link NodeIterable} and + * {@link EdgeIterable}) before it is exhausted or {@code doBreak()} has been called. Use + * {@link #tryReadLock(long, TimeUnit)} when the caller cannot afford to wait indefinitely. */ void readLock(); @@ -44,6 +52,7 @@ public interface GraphLock { * and returns immediately, setting the write lock hold count to one. * * @throws IllegalMonitorStateException if the current thread holds a read lock already + * @see #tryWriteLock(long, TimeUnit) */ void writeLock(); @@ -73,4 +82,72 @@ public interface GraphLock { * current thread */ int getWriteHoldCount(); + + /** + * Acquires the read lock if the write lock is not held by another thread within the given waiting time. + *

+ * Unlike {@link #readLock()}, the wait is bounded and interruptible. A caller that receives {@code false} has not + * acquired the lock and must not call {@link #readUnlock()}. + * + * @param timeout the time to wait for the read lock + * @param unit the time unit of the timeout argument + * @return true if the read lock was acquired + * @throws InterruptedException if the current thread is interrupted while waiting + * @throws UnsupportedOperationException if the implementation does not support timed acquisition + */ + default boolean tryReadLock(long timeout, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + /** + * Acquires the write lock if neither the read nor write lock are held by another thread within the given waiting + * time. + *

+ * Unlike {@link #writeLock()}, the wait is bounded and interruptible. A caller that receives {@code false} has not + * acquired the lock and must not call {@link #writeUnlock()}. + * + * @param timeout the time to wait for the write lock + * @param unit the time unit of the timeout argument + * @return true if the write lock was acquired + * @throws IllegalMonitorStateException if the current thread holds a read lock already + * @throws InterruptedException if the current thread is interrupted while waiting + * @throws UnsupportedOperationException if the implementation does not support timed acquisition + */ + default boolean tryWriteLock(long timeout, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + /** + * Queries the number of read holds on this lock across all threads. This differs from {@link #getReadHoldCount()}, + * which counts only the current thread. A non-zero value while no thread is expected to be reading points at a read + * hold that was never released. + * + * @return the total number of read holds, or zero if the read lock is not held + * @throws UnsupportedOperationException if the implementation does not expose this + */ + default int getReadLockCount() { + throw new UnsupportedOperationException(); + } + + /** + * Queries whether the write lock is held by any thread. + * + * @return true if any thread holds the write lock + * @throws UnsupportedOperationException if the implementation does not expose this + */ + default boolean isWriteLocked() { + throw new UnsupportedOperationException(); + } + + /** + * Returns an estimate of the number of threads waiting to acquire either the read or the write lock. The value is + * an estimate because the number of threads may change while this method traverses internal data structures. It is + * designed for monitoring, not for synchronization control. + * + * @return the estimated number of waiting threads + * @throws UnsupportedOperationException if the implementation does not expose this + */ + default int getQueueLength() { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/org/gephi/graph/impl/GraphLockImpl.java b/src/main/java/org/gephi/graph/impl/GraphLockImpl.java index c2bca26c..980c6102 100644 --- a/src/main/java/org/gephi/graph/impl/GraphLockImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphLockImpl.java @@ -15,6 +15,7 @@ */ package org.gephi.graph.impl; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; @@ -74,6 +75,35 @@ public int getWriteHoldCount() { return readWriteLock.getWriteHoldCount(); } + @Override + public boolean tryReadLock(long timeout, TimeUnit unit) throws InterruptedException { + return readLock.tryLock(timeout, unit); + } + + @Override + public boolean tryWriteLock(long timeout, TimeUnit unit) throws InterruptedException { + if (readWriteLock.getReadHoldCount() > 0 && !readWriteLock.isWriteLockedByCurrentThread()) { + throw new IllegalMonitorStateException( + "Impossible to acquire a write lock when currently holding a read lock. Use toArray() methods on NodeIterable and EdgeIterable to avoid holding a readLock or wrap your loop with a write lock."); + } + return writeLock.tryLock(timeout, unit); + } + + @Override + public int getReadLockCount() { + return readWriteLock.getReadLockCount(); + } + + @Override + public boolean isWriteLocked() { + return readWriteLock.isWriteLocked(); + } + + @Override + public int getQueueLength() { + return readWriteLock.getQueueLength(); + } + public void checkHoldWriteLock() { if (!readWriteLock.isWriteLockedByCurrentThread()) { throw new IllegalMonitorStateException( diff --git a/src/test/java/org/gephi/graph/impl/GraphLockImplTest.java b/src/test/java/org/gephi/graph/impl/GraphLockImplTest.java index bda4193d..2b65bd97 100644 --- a/src/test/java/org/gephi/graph/impl/GraphLockImplTest.java +++ b/src/test/java/org/gephi/graph/impl/GraphLockImplTest.java @@ -15,6 +15,9 @@ */ package org.gephi.graph.impl; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.testng.Assert; import org.testng.annotations.Test; @@ -71,4 +74,163 @@ public void testHoldersCount() { Assert.assertEquals(lock.getWriteHoldCount(), 0); } + + // --- Timed acquisition --- + + @Test + public void testTryWriteLockAcquiresWhenFree() throws InterruptedException { + GraphLockImpl lock = new GraphLockImpl(); + Assert.assertTrue(lock.tryWriteLock(1, TimeUnit.SECONDS)); + Assert.assertEquals(lock.getWriteHoldCount(), 1); + lock.writeUnlock(); + } + + @Test + public void testTryWriteLockTimesOutWhileAnotherThreadHoldsRead() throws InterruptedException { + GraphLockImpl lock = new GraphLockImpl(); + CountDownLatch acquired = new CountDownLatch(1); + CountDownLatch release = new CountDownLatch(1); + Thread reader = holdLockOnThread(lock, false, acquired, release); + acquired.await(); + Assert.assertFalse(lock.tryWriteLock(100, TimeUnit.MILLISECONDS)); + Assert.assertEquals(lock.getWriteHoldCount(), 0); + release.countDown(); + reader.join(); + Assert.assertTrue(lock.tryWriteLock(1, TimeUnit.SECONDS)); + lock.writeUnlock(); + } + + @Test(expectedExceptions = IllegalMonitorStateException.class) + public void testTryWriteLockWhileHoldingReadLock() throws InterruptedException { + GraphLockImpl lock = new GraphLockImpl(); + lock.readLock(); + lock.tryWriteLock(1, TimeUnit.SECONDS); + } + + @Test + public void testTryReadLockAcquiresWhenFree() throws InterruptedException { + GraphLockImpl lock = new GraphLockImpl(); + Assert.assertTrue(lock.tryReadLock(1, TimeUnit.SECONDS)); + Assert.assertEquals(lock.getReadHoldCount(), 1); + lock.readUnlock(); + } + + @Test + public void testTryReadLockTimesOutWhileAnotherThreadHoldsWrite() throws InterruptedException { + GraphLockImpl lock = new GraphLockImpl(); + CountDownLatch acquired = new CountDownLatch(1); + CountDownLatch release = new CountDownLatch(1); + Thread writer = holdLockOnThread(lock, true, acquired, release); + acquired.await(); + Assert.assertFalse(lock.tryReadLock(100, TimeUnit.MILLISECONDS)); + Assert.assertEquals(lock.getReadHoldCount(), 0); + release.countDown(); + writer.join(); + Assert.assertTrue(lock.tryReadLock(1, TimeUnit.SECONDS)); + lock.readUnlock(); + } + + @Test + public void testTryWriteLockPropagatesInterrupt() throws InterruptedException { + GraphLockImpl lock = new GraphLockImpl(); + CountDownLatch acquired = new CountDownLatch(1); + CountDownLatch release = new CountDownLatch(1); + Thread reader = holdLockOnThread(lock, false, acquired, release); + acquired.await(); + AtomicBoolean interrupted = new AtomicBoolean(false); + CountDownLatch waiting = new CountDownLatch(1); + Thread writer = new Thread(() -> { + try { + waiting.countDown(); + lock.tryWriteLock(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + interrupted.set(true); + } + }); + writer.start(); + waiting.await(); + writer.interrupt(); + writer.join(5000); + Assert.assertFalse(writer.isAlive()); + Assert.assertTrue(interrupted.get()); + Assert.assertEquals(lock.getWriteHoldCount(), 0); + release.countDown(); + reader.join(); + } + + // --- Diagnostics --- + + @Test + public void testGetQueueLengthReportsQueuedWriter() throws InterruptedException { + GraphLockImpl lock = new GraphLockImpl(); + lock.readLock(); + Assert.assertEquals(lock.getQueueLength(), 0); + Thread writer = new Thread(() -> { + lock.writeLock(); + lock.writeUnlock(); + }); + writer.start(); + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5); + while (lock.getQueueLength() == 0 && System.nanoTime() < deadline) { + Thread.sleep(5); + } + Assert.assertEquals(lock.getQueueLength(), 1); + lock.readUnlock(); + writer.join(); + Assert.assertEquals(lock.getQueueLength(), 0); + } + + @Test + public void testGetReadLockCountCountsHoldsAcrossThreads() throws InterruptedException { + GraphLockImpl lock = new GraphLockImpl(); + Assert.assertEquals(lock.getReadLockCount(), 0); + lock.readLock(); + CountDownLatch acquired = new CountDownLatch(1); + CountDownLatch release = new CountDownLatch(1); + Thread reader = holdLockOnThread(lock, false, acquired, release); + acquired.await(); + Assert.assertEquals(lock.getReadLockCount(), 2); + Assert.assertEquals(lock.getReadHoldCount(), 1); + release.countDown(); + reader.join(); + Assert.assertEquals(lock.getReadLockCount(), 1); + lock.readUnlock(); + Assert.assertEquals(lock.getReadLockCount(), 0); + } + + @Test + public void testIsWriteLocked() { + GraphLockImpl lock = new GraphLockImpl(); + Assert.assertFalse(lock.isWriteLocked()); + lock.writeLock(); + Assert.assertTrue(lock.isWriteLocked()); + lock.writeUnlock(); + Assert.assertFalse(lock.isWriteLocked()); + } + + // Holds the read (or write) lock on a background thread until released, so the test thread can observe contention. + private static Thread holdLockOnThread(GraphLockImpl lock, boolean write, CountDownLatch acquired, CountDownLatch release) { + Thread t = new Thread(() -> { + if (write) { + lock.writeLock(); + } else { + lock.readLock(); + } + acquired.countDown(); + try { + release.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } finally { + if (write) { + lock.writeUnlock(); + } else { + lock.readUnlock(); + } + } + }); + t.setDaemon(true); + t.start(); + return t; + } }