New Upstream Release - libbeam-java

Ready changes

Summary

Merged new upstream version: 1.3.5 (was: 1.3.3).

Resulting package

Built on 2023-04-09T17:33 (took 16m36s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases libbeam-java

Lintian Result

Diff

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..e324d3e
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,36 @@
+name: Build
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    types: [opened, synchronize, reopened]
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0  # Shallow clones should be disabled for a better relevancy of analysis
+      - name: Set up JDK 11
+        uses: actions/setup-java@v1
+        with:
+          java-version: 11
+      - name: Cache SonarCloud packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.sonar/cache
+          key: ${{ runner.os }}-sonar
+          restore-keys: ${{ runner.os }}-sonar
+      - name: Cache Maven packages
+        uses: actions/cache@v1
+        with:
+          path: ~/.m2
+          key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+          restore-keys: ${{ runner.os }}-m2
+      - name: Build and analyze
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any
+          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+        run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=johnmay_beam -Pcoverage
\ No newline at end of file
diff --git a/core/pom.xml b/core/pom.xml
index 9c04219..3281abc 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <artifactId>beam</artifactId>
         <groupId>uk.ac.ebi.beam</groupId>
-        <version>1.3.3</version>
+        <version>1.3.5</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -16,19 +16,16 @@
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
-            <version>4.11</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.hamcrest</groupId>
-            <artifactId>hamcrest-all</artifactId>
-            <version>1.3</version>
+            <artifactId>hamcrest-core</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
-            <version>1.9.5</version>
+            <artifactId>mockito-core</artifactId>
             <scope>test</scope>
         </dependency>
     </dependencies>
diff --git a/core/src/main/java/uk/ac/ebi/beam/GraphBuilder.java b/core/src/main/java/uk/ac/ebi/beam/GraphBuilder.java
index 01448e4..4941e84 100644
--- a/core/src/main/java/uk/ac/ebi/beam/GraphBuilder.java
+++ b/core/src/main/java/uk/ac/ebi/beam/GraphBuilder.java
@@ -304,6 +304,8 @@ public final class GraphBuilder {
 
             Edge eLab = findBondToLabel(g, builder.u);
             Edge fLab = findBondToLabel(g, builder.v);
+            if (eLab == null || fLab == null)
+                continue;
 
             // adjust for reference
             Configuration.DoubleBond config = builder.c;
diff --git a/core/src/main/java/uk/ac/ebi/beam/Localise.java b/core/src/main/java/uk/ac/ebi/beam/Localise.java
index ac18eae..846e345 100644
--- a/core/src/main/java/uk/ac/ebi/beam/Localise.java
+++ b/core/src/main/java/uk/ac/ebi/beam/Localise.java
@@ -159,8 +159,9 @@ final class Localise {
             for (int j = 0; j < d; ++j) {
                 Edge e = g.edgeAt(v, j);
                 if (e.bond() == Bond.DOUBLE) {
-                    if (q == 0 && (a.element() == Element.Nitrogen || (a.element() == Element.Sulfur && deg > 3))
-                            )
+                    if (q == 0 && (a.element() == Element.Nitrogen ||
+                                   a.element() == Element.Phosphorus ||
+                                  (a.element() == Element.Sulfur && deg > 3)))
                         return false;
                     return true;
                 }
@@ -258,10 +259,12 @@ final class Localise {
 
                             if (hasAdjDirectionalLabels(g, e, cyclic) && !inSmallRing(g, e)) {
                                 other = -1;
+                                target = null;
                                 break;
                             }
                             if (vExtra > 1 && hasAdditionalCyclicDoubleBond(g, cyclic, u, v)) {
                                 other = -1;
+                                target = null;
                                 break;
                             }
                             if (other == -1) {
@@ -269,6 +272,7 @@ final class Localise {
                                 target = e;
                             } else {
                                 other = -2; // found more than one
+                                target = null;
                             }
                         }
                         // only one double bond don't check any more
@@ -277,7 +281,7 @@ final class Localise {
                     }
                 }
 
-                if (other >= 0) {
+                if (target != null) {
                     subset.set(u);
                     subset.set(other);
                     target.bond(Bond.IMPLICIT);
diff --git a/core/src/main/java/uk/ac/ebi/beam/Parser.java b/core/src/main/java/uk/ac/ebi/beam/Parser.java
index 1cee6ee..3a1a1dd 100644
--- a/core/src/main/java/uk/ac/ebi/beam/Parser.java
+++ b/core/src/main/java/uk/ac/ebi/beam/Parser.java
@@ -386,8 +386,34 @@ final class Parser {
                 us = insertDbImplicitRef(u, us); // XXX: temp fix
             else if (c.type() == Configuration.Type.ExtendedTetrahedral) {
                 g.addFlags(Graph.HAS_EXT_STRO);
-                if ((us = getAlleneCarriers(u)) == null)
-                    return;
+                if ((us = getAlleneCarriers(u)) == null) {
+                  if (strict)
+                    throw new InvalidSmilesException("Invalid Allene stereo");
+                  else
+                    warnings.add("Ignored invalid Allene stereochemistry");
+                  return;
+                }
+            } else if (c.type() == Configuration.Type.SquarePlanar &&
+                       us.length != 4) {
+              if (strict)
+                throw new InvalidSmilesException("SquarePlanar without 4 explicit neighbours");
+              else
+                warnings.add("SquarePlanar without 4 explicit neighbours");
+              return;
+            } else if (c.type() == Configuration.Type.TrigonalBipyramidal &&
+                       us.length != 5) {
+              if (strict)
+                throw new InvalidSmilesException("SquarePlanar without 5 explicit neighbours");
+              else
+                warnings.add("SquarePlanar without 5 explicit neighbours");
+              return;
+            } else if (c.type() == Configuration.Type.Octahedral &&
+                       us.length != 6) {
+              if (strict)
+                throw new InvalidSmilesException("SquarePlanar without 6 explicit neighbours");
+              else
+                warnings.add("SquarePlanar without 6 explicit neighbours");
+              return;
             }
             g.addTopology(Topology.create(u, us, es, c));
         }
@@ -680,7 +706,7 @@ final class Parser {
                         sb.append(c);
                     }
                     g.setTitle(sb.toString());
-
+                    return;
                 case '\n':
                 case '\r':
                     return;
diff --git a/core/src/test/java/uk/ac/ebi/beam/AtomCentricDBConfigTest.java b/core/src/test/java/uk/ac/ebi/beam/AtomCentricDBConfigTest.java
index f1c6e9c..eb54b0b 100644
--- a/core/src/test/java/uk/ac/ebi/beam/AtomCentricDBConfigTest.java
+++ b/core/src/test/java/uk/ac/ebi/beam/AtomCentricDBConfigTest.java
@@ -77,17 +77,17 @@ public class AtomCentricDBConfigTest {
     }
 
     @Test public void difluoroethene() throws InvalidSmilesException {
-        GeneratorTest.roundTrip("F[C@H]=[C@H]F");
-        GeneratorTest.roundTrip("F[C@@H]=[C@@H]F");
-        GeneratorTest.roundTrip("F[C@H]=[C@@H]F");
-        GeneratorTest.roundTrip("F[C@@H]=[C@H]F");
+        GeneratorTest.assertRoundTrip("F[C@H]=[C@H]F");
+        GeneratorTest.assertRoundTrip("F[C@@H]=[C@@H]F");
+        GeneratorTest.assertRoundTrip("F[C@H]=[C@@H]F");
+        GeneratorTest.assertRoundTrip("F[C@@H]=[C@H]F");
     }
 
     @Test public void difluoroethene_permute() throws InvalidSmilesException {
-        GeneratorTest.roundTrip("F[C@H]=[C@H]F",
+        GeneratorTest.assertRoundTrip("F[C@H]=[C@H]F",
                                 new int[]{1, 0, 2, 3},
                                 "[C@@H](F)=[C@H]F");
-        GeneratorTest.roundTrip("[C@@H](F)=[C@H]F",
+        GeneratorTest.assertRoundTrip("[C@@H](F)=[C@H]F",
                                 new int[]{1, 0, 2, 3},
                                 "F[C@H]=[C@H]F");
     }
diff --git a/core/src/test/java/uk/ac/ebi/beam/ConfigurationTest.java b/core/src/test/java/uk/ac/ebi/beam/ConfigurationTest.java
index 517b137..8fd3235 100644
--- a/core/src/test/java/uk/ac/ebi/beam/ConfigurationTest.java
+++ b/core/src/test/java/uk/ac/ebi/beam/ConfigurationTest.java
@@ -131,7 +131,8 @@ public class ConfigurationTest {
     }
 
     @Test public void antiClockwise() throws InvalidSmilesException {
-        Configuration.read(CharBuffer.fromString("@H"));
+        assertThat(Configuration.read(CharBuffer.fromString("@H")),
+                   is(Configuration.ANTI_CLOCKWISE));
     }
 
     @Test(expected = InvalidSmilesException.class)
diff --git a/core/src/test/java/uk/ac/ebi/beam/GeneratorTest.java b/core/src/test/java/uk/ac/ebi/beam/GeneratorTest.java
index dc79853..512f82c 100644
--- a/core/src/test/java/uk/ac/ebi/beam/GeneratorTest.java
+++ b/core/src/test/java/uk/ac/ebi/beam/GeneratorTest.java
@@ -45,79 +45,79 @@ public class GeneratorTest {
 
     @Test public void implicitHCentre() throws InvalidSmilesException {
 
-        roundTrip("[C@@H](N)(O)C");
+        assertRoundTrip("[C@@H](N)(O)C");
 
         // permutations
-        roundTrip("[C@@H](N)(O)C", new int[]{0, 1, 2, 3}, "[C@@H](N)(O)C");
-        roundTrip("[C@@H](N)(O)C", new int[]{0, 1, 3, 2}, "[C@H](N)(C)O");
-        roundTrip("[C@@H](N)(O)C", new int[]{0, 2, 1, 3}, "[C@H](O)(N)C");
-        roundTrip("[C@@H](N)(O)C", new int[]{0, 2, 3, 1}, "[C@@H](C)(N)O");
-        roundTrip("[C@@H](N)(O)C", new int[]{0, 3, 1, 2}, "[C@@H](O)(C)N");
-        roundTrip("[C@@H](N)(O)C", new int[]{0, 3, 2, 1}, "[C@H](C)(O)N");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{0, 1, 2, 3}, "[C@@H](N)(O)C");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{0, 1, 3, 2}, "[C@H](N)(C)O");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{0, 2, 1, 3}, "[C@H](O)(N)C");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{0, 2, 3, 1}, "[C@@H](C)(N)O");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{0, 3, 1, 2}, "[C@@H](O)(C)N");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{0, 3, 2, 1}, "[C@H](C)(O)N");
 
-        roundTrip("[C@@H](N)(O)C", new int[]{1, 0, 2, 3}, "N[C@H](O)C");
-        roundTrip("[C@@H](N)(O)C", new int[]{1, 0, 3, 2}, "N[C@@H](C)O");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{1, 0, 2, 3}, "N[C@H](O)C");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{1, 0, 3, 2}, "N[C@@H](C)O");
 
-        roundTrip("[C@@H](N)(O)C", new int[]{1, 2, 0, 3}, "O[C@@H](N)C");
-        roundTrip("[C@@H](N)(O)C", new int[]{1, 3, 0, 2}, "O[C@H](C)N");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{1, 2, 0, 3}, "O[C@@H](N)C");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{1, 3, 0, 2}, "O[C@H](C)N");
 
-        roundTrip("[C@@H](N)(O)C", new int[]{1, 2, 3, 0}, "C[C@H](N)O");
-        roundTrip("[C@@H](N)(O)C", new int[]{1, 3, 2, 0}, "C[C@@H](O)N");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{1, 2, 3, 0}, "C[C@H](N)O");
+        assertRoundTrip("[C@@H](N)(O)C", new int[]{1, 3, 2, 0}, "C[C@@H](O)N");
 
-        roundTrip("[C@H](N)(C)O");
+        assertRoundTrip("[C@H](N)(C)O");
 
-        roundTrip("[C@H](N)(C)O", new int[]{0, 1, 2, 3}, "[C@H](N)(C)O");
-        roundTrip("[C@H](N)(C)O", new int[]{0, 1, 3, 2}, "[C@@H](N)(O)C");
-        roundTrip("[C@H](N)(C)O", new int[]{0, 2, 1, 3}, "[C@@H](C)(N)O");
-        roundTrip("[C@H](N)(C)O", new int[]{0, 2, 3, 1}, "[C@H](O)(N)C");
-        roundTrip("[C@H](N)(C)O", new int[]{0, 3, 1, 2}, "[C@H](C)(O)N");
-        roundTrip("[C@H](N)(C)O", new int[]{0, 3, 2, 1}, "[C@@H](O)(C)N");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{0, 1, 2, 3}, "[C@H](N)(C)O");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{0, 1, 3, 2}, "[C@@H](N)(O)C");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{0, 2, 1, 3}, "[C@@H](C)(N)O");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{0, 2, 3, 1}, "[C@H](O)(N)C");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{0, 3, 1, 2}, "[C@H](C)(O)N");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{0, 3, 2, 1}, "[C@@H](O)(C)N");
 
-        roundTrip("[C@H](N)(C)O", new int[]{1, 0, 2, 3}, "N[C@@H](C)O");
-        roundTrip("[C@H](N)(C)O", new int[]{1, 0, 3, 2}, "N[C@H](O)C");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{1, 0, 2, 3}, "N[C@@H](C)O");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{1, 0, 3, 2}, "N[C@H](O)C");
 
-        roundTrip("[C@H](N)(C)O", new int[]{1, 2, 0, 3}, "C[C@H](N)O");
-        roundTrip("[C@H](N)(C)O", new int[]{1, 3, 0, 2}, "C[C@@H](O)N");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{1, 2, 0, 3}, "C[C@H](N)O");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{1, 3, 0, 2}, "C[C@@H](O)N");
 
-        roundTrip("[C@H](N)(C)O", new int[]{1, 2, 3, 0}, "O[C@@H](N)C");
-        roundTrip("[C@H](N)(C)O", new int[]{1, 3, 2, 0}, "O[C@H](C)N");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{1, 2, 3, 0}, "O[C@@H](N)C");
+        assertRoundTrip("[C@H](N)(C)O", new int[]{1, 3, 2, 0}, "O[C@H](C)N");
 
-        roundTrip("N[C@@H](C)O");
-        roundTrip("N[C@@H](C)O");
-        roundTrip("N[C@H](O)C");
-        roundTrip("O[C@@H](N)C");
-        roundTrip("O[C@H](C)N");
-        roundTrip("C[C@@H](O)N");
-        roundTrip("C[C@H](N)O");
+        assertRoundTrip("N[C@@H](C)O");
+        assertRoundTrip("N[C@@H](C)O");
+        assertRoundTrip("N[C@H](O)C");
+        assertRoundTrip("O[C@@H](N)C");
+        assertRoundTrip("O[C@H](C)N");
+        assertRoundTrip("C[C@@H](O)N");
+        assertRoundTrip("C[C@H](N)O");
     }
 
     @Test public void ring_closures1() throws Exception {
-        roundTrip("C1=CN=CC2=NC=N[C@@H]21");
+        assertRoundTrip("C1=CN=CC2=NC=N[C@@H]21");
     }
 
     @Test public void ring_closures2() throws Exception {
-        roundTrip("C1=CN=CC2=NC=N[C@H]21");
+        assertRoundTrip("C1=CN=CC2=NC=N[C@H]21");
     }
 
     @Test public void ring_closures3() throws Exception {
-        roundTrip("C1=CC(=CC2=NC(=N[C@@H]21)C(F)(F)F)N");
+        assertRoundTrip("C1=CC(=CC2=NC(=N[C@@H]21)C(F)(F)F)N");
     }
 
     @Test public void ring_closures4() throws Exception {
-        roundTrip("C1=CC(=CC2=NC(=N[C@H]21)C(F)(F)F)N");
+        assertRoundTrip("C1=CC(=CC2=NC(=N[C@H]21)C(F)(F)F)N");
     }
 
 
     @Test public void lowRingNumberOrder() throws InvalidSmilesException {
-        roundTrip("C1=CC2=CC=CC=C2C=C1");
+        assertRoundTrip("C1=CC2=CC=CC=C2C=C1");
     }
 
     @Test public void multipleRingNumberOrder() throws InvalidSmilesException {
-        roundTrip("C1=CC2=C3C4=C5C(C=CC6=C5C7=C(C=C6)C=CC(C=C2)=C37)=CC=C14");
+        assertRoundTrip("C1=CC2=C3C4=C5C(C=CC6=C5C7=C(C=C6)C=CC(C=C2)=C37)=CC=C14");
     }
 
     @Test public void highRingNumberOrder() throws InvalidSmilesException {
-        roundTrip("C1CC2CCC3=C4C2=C5C1CCC6=C5C7=C8C(C=C9CCC%10CCC%11CCC%12=CC(=C3)C(C%13=C8C9=C%10C%11=C%12%13)=C47)=C6");
+        assertRoundTrip("C1CC2CCC3=C4C2=C5C1CCC6=C5C7=C8C(C=C9CCC%10CCC%11CCC%12=CC(=C3)C(C%13=C8C9=C%10C%11=C%12%13)=C47)=C6");
     }
 
     @Test public void bondTypeOnFirstAtom1() throws InvalidSmilesException {
@@ -166,11 +166,11 @@ public class GeneratorTest {
     }
 
     @Test public void sodiumChloride() throws InvalidSmilesException {
-        roundTrip("[Na+].[Cl-]");
+        assertRoundTrip("[Na+].[Cl-]");
     }
 
     @Test public void disconnected() throws InvalidSmilesException {
-        roundTrip("CCCC.OOOO.C[CH]C.CNO");
+        assertRoundTrip("CCCC.OOOO.C[CH]C.CNO");
     }
     
     @Test public void extendedTetrhedral_al1() throws Exception {
@@ -301,11 +301,11 @@ public class GeneratorTest {
         assertThat(g.toSmiles(), is("CC=[C@]=C1OCCCC1"));
     }
 
-    static void roundTrip(String smi) throws InvalidSmilesException {
+    static void assertRoundTrip(String smi) throws InvalidSmilesException {
         assertThat(Generator.generate(Parser.parse(smi)), is(smi));
     }
 
-    static void roundTrip(String smi, int[] p, String res) throws
+    static void assertRoundTrip(String smi, int[] p, String res) throws
                                                            InvalidSmilesException {
         assertThat(Generator.generate(Parser.parse(smi).permute(p)), is(res));
     }
diff --git a/core/src/test/java/uk/ac/ebi/beam/GraphBuilderTest.java b/core/src/test/java/uk/ac/ebi/beam/GraphBuilderTest.java
index f0620c4..e0b3c43 100644
--- a/core/src/test/java/uk/ac/ebi/beam/GraphBuilderTest.java
+++ b/core/src/test/java/uk/ac/ebi/beam/GraphBuilderTest.java
@@ -35,6 +35,7 @@ import org.junit.Test;
 import java.io.IOException;
 
 import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static uk.ac.ebi.beam.Configuration.DoubleBond.OPPOSITE;
 
@@ -212,6 +213,7 @@ public class GraphBuilderTest {
                     .geometric(7, 6).configure(8, 3, OPPOSITE)
                     .geometric(3, 4).configure(2, 5, OPPOSITE)
                     .build();
+        assertNotNull(g); // g builds okay
     }
 
     @Test
@@ -301,6 +303,7 @@ public class GraphBuilderTest {
                     .geometric(8, 9).opposite(4, 10)
                     .geometric(3, 4).opposite(2, 8)
                     .build();
+        assertNotNull(g); // builds okay
     }
 
     @Test
@@ -337,6 +340,7 @@ public class GraphBuilderTest {
                .neighbors(2, 3, 4)
                .winding(Configuration.AL1)
                .build();
+        assertNotNull(gb.build()); // builds okay
     }
 
     @Test
diff --git a/core/src/test/java/uk/ac/ebi/beam/GraphTest.java b/core/src/test/java/uk/ac/ebi/beam/GraphTest.java
index e3bad3d..d118f0d 100644
--- a/core/src/test/java/uk/ac/ebi/beam/GraphTest.java
+++ b/core/src/test/java/uk/ac/ebi/beam/GraphTest.java
@@ -220,7 +220,9 @@ public class GraphTest {
     @Test public void addUnknownTopology() {
         Topology t = Topology.unknown();
         Graph g = new Graph(5);
-        g.addTopology(t);
+        g.addTopology(t); // don't fail
+        assertThat(g.order(), is(0));
+        assertThat(g.size(), is(0));
     }
 
     @Test public void defaultTopology() {
@@ -479,6 +481,7 @@ public class GraphTest {
     
     @Test public void CHEMBL1215012() throws Exception {
         Graph g = Graph.fromSmiles("[Na+].[Na+].CC(C)c1c(O)c(O)c(\\C=N\\[C@H]2[C@H]3SC(C)(C)[C@@H](N3C2=O)C(=O)[O-])c4C(=O)C(=C(C)C(=O)c14)C5=C(C)C(=O)c6c(C(C)C)c(O)c(O)c(\\C=N\\[C@H]7[C@H]8SC(C)(C)[C@@H](N8C7=O)C(=O)[O-])c6C5=O CHEMBL1215012");
+        Assert.assertNotNull(g);
     }
 
     @Test public void nitgrogenStereochemistry() throws Exception {
@@ -542,4 +545,10 @@ public class GraphTest {
         Graph g = Graph.fromSmiles("[CH3:3]1.[CH3:1]C(=[C@@]=[CH:2]1)[CH2:4]C");
         Assert.assertThat(g.topologyOf(3).configuration(), is(Configuration.AL2));
     }
+
+    @Test public void nofail() throws IOException {
+        Graph g = Graph.fromSmiles("CCCO[P@H]1(OC[C@@H]2[C@@H](O1)[C@@]([C@@H](O2)n3cnc4c3nc(nc4OCC)N)(C)F)O CHEMBL1630021");
+        assertThat(g.toSmiles(),
+                   CoreMatchers.is("CCCO[PH]1(OC[C@@H]2[C@@H](O1)[C@@]([C@@H](O2)n3cnc4c3nc(nc4OCC)N)(C)F)O"));
+    }
 }
diff --git a/core/src/test/java/uk/ac/ebi/beam/LocaliseTest.java b/core/src/test/java/uk/ac/ebi/beam/LocaliseTest.java
index 963a6e3..a92e791 100644
--- a/core/src/test/java/uk/ac/ebi/beam/LocaliseTest.java
+++ b/core/src/test/java/uk/ac/ebi/beam/LocaliseTest.java
@@ -385,6 +385,10 @@ public class LocaliseTest {
         test("*1ccccc1", "*1=CC=CC=C1");
     }
 
+    @Test public void aromphos() throws Exception {
+      test("O=p1ccccc1", "O=P1=CC=CC=C1");
+    }
+
     static void test(String delocalised, String localised) throws Exception {
         Graph g = Graph.fromSmiles(delocalised);
         Graph h = Localise.localise(g);
diff --git a/core/src/test/java/uk/ac/ebi/beam/ParserTest.java b/core/src/test/java/uk/ac/ebi/beam/ParserTest.java
index d9e6077..3c16959 100644
--- a/core/src/test/java/uk/ac/ebi/beam/ParserTest.java
+++ b/core/src/test/java/uk/ac/ebi/beam/ParserTest.java
@@ -36,9 +36,8 @@ import org.junit.Test;
 import java.io.IOException;
 
 import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
+import static org.junit.Assert.assertNotNull;
 
 /** @author John May */
 public class ParserTest {
@@ -96,7 +95,9 @@ public class ParserTest {
     }
 
     @Test public void tellurophene() throws InvalidSmilesException {
-        Parser.parse("c1cc[te]c1");
+        Graph g = Parser.parse("c1cc[te]c1");
+        assertThat(g.order(), is(5));
+        assertThat(g.size(), is(5));
     }
 
     @Test public void mixingAromaticAndKekule() throws InvalidSmilesException {
@@ -142,6 +143,8 @@ public class ParserTest {
     @Test
     public void hydrogen_strict_okay() throws IOException {
         Graph g = Parser.strict("[H][H]");
+        assertNotNull(g);
+        assertThat(g.order(), is(2));
     }
 
     @Test public void tellurium() throws IOException {
@@ -157,6 +160,8 @@ public class ParserTest {
 
     @Test public void largeRnum() throws Exception {
         Graph g = Parser.parse("C%99CCCC%99");
+        assertThat(g.order(), is(5));
+        assertThat(g.size(), is(5));
     }
 
     // not part of spec
@@ -215,7 +220,7 @@ public class ParserTest {
 
     @Test
     public void acceptMultipleBonds() throws Exception {
-        Parser.parse("C/C=C/C\\C=C/C");
+        assertNotNull(Parser.parse("C/C=C/C\\C=C/C"));
     }
     
     @Test
diff --git a/debian/changelog b/debian/changelog
index 38e6a32..6784993 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+libbeam-java (1.3.5-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sun, 09 Apr 2023 17:18:20 -0000
+
 libbeam-java (1.3.3-3) unstable; urgency=medium
 
   * Executing build time tests.
diff --git a/exec/pom.xml b/exec/pom.xml
index 92b111d..8e629a6 100644
--- a/exec/pom.xml
+++ b/exec/pom.xml
@@ -5,7 +5,7 @@
   <parent>
     <artifactId>beam</artifactId>
     <groupId>uk.ac.ebi.beam</groupId>
-    <version>1.3.3</version>
+    <version>1.3.5</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
diff --git a/exec/src/main/java/uk/ac/ebi/beam/FunctorCmdLnModule.java b/exec/src/main/java/uk/ac/ebi/beam/FunctorCmdLnModule.java
index eb8dd05..8641bfe 100644
--- a/exec/src/main/java/uk/ac/ebi/beam/FunctorCmdLnModule.java
+++ b/exec/src/main/java/uk/ac/ebi/beam/FunctorCmdLnModule.java
@@ -27,7 +27,7 @@ abstract class FunctorCmdLnModule extends PipingCmdLnModule {
      * How much input we process at once, could be adjustable.
      */
     final int WORK_UNIT_SIZE = 15000;
-    final boolean debug = false;
+    static final boolean DEBUG = false;
 
     FunctorCmdLnModule(String name) {
         super(name);
@@ -89,7 +89,6 @@ abstract class FunctorCmdLnModule extends PipingCmdLnModule {
                                                elapsedMilli(tStart)), cnt);
                 }
             } catch (Exception | InternalError e) {
-                if (debug) e.printStackTrace();
                 if (showWarnings) {
                     report("error, " + e.getMessage() + "\nline:" + escapeForPrintf(line) + "\n");
                 }
@@ -186,7 +185,7 @@ abstract class FunctorCmdLnModule extends PipingCmdLnModule {
         try {
             Thread.sleep(ms);
         } catch (InterruptedException e) {
-            // ignore
+            Thread.currentThread().interrupt();
         }
     }
     
@@ -197,10 +196,11 @@ abstract class FunctorCmdLnModule extends PipingCmdLnModule {
     private Result getResult(Future<Result> future) {
         try {
             return future.get();
-        } catch (InterruptedException | ExecutionException e) {
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } catch (ExecutionException e) {
             System.err.println(e.getMessage());
-            if (debug) 
-                e.printStackTrace();
+
         }
         return null;
     }
@@ -221,7 +221,7 @@ abstract class FunctorCmdLnModule extends PipingCmdLnModule {
             }
             return cnt;
         } catch (IOException e) {
-            e.printStackTrace();
+            System.err.println("IO Error: " + e.getMessage());
         }
         return 0;
     }
@@ -270,7 +270,6 @@ abstract class FunctorCmdLnModule extends PipingCmdLnModule {
                 } catch (Exception e) {
                     if (warn) {
                         report("\nerror, " + e.getMessage() + "\nline:" + escapeForPrintf(lines.get(i)) + "\n");
-                        if (debug) e.printStackTrace();
                     }
                     lines.set(i, null);
                 }
diff --git a/exec/src/main/java/uk/ac/ebi/beam/PipingCmdLnModule.java b/exec/src/main/java/uk/ac/ebi/beam/PipingCmdLnModule.java
index f199228..ac3bbaf 100644
--- a/exec/src/main/java/uk/ac/ebi/beam/PipingCmdLnModule.java
+++ b/exec/src/main/java/uk/ac/ebi/beam/PipingCmdLnModule.java
@@ -68,7 +68,7 @@ public abstract class PipingCmdLnModule implements CmdLnModule {
         try {
             process(args);
         } catch (IOException e) {
-            e.printStackTrace();
+            System.err.println("Execution error: " + e.getMessage());
         }
     }
 
@@ -81,25 +81,24 @@ public abstract class PipingCmdLnModule implements CmdLnModule {
         final File fin = nonopt.size() > 0 ? new File(nonopt.get(0).toString()) 
                                            : null;
 
-        InputStream in = fin == null ? System.in : new CountingInputStream(fin);
-        OutputStream out = nonopt.size() < 2 ? System.out
-                                             : new FileOutputStream(nonopt.get(1).toString());
-
+        try (InputStream in = fin == null ? System.in : new CountingInputStream(fin);
+             OutputStream out = nonopt.size() < 2 ? System.out : new FileOutputStream(nonopt.get(1).toString());
+             BufferedWriter bwtr = new BufferedWriter(new OutputStreamWriter(out, UTF_8));
+             BufferedReader brdr = new BufferedReader(new InputStreamReader(in, UTF_8))) {
 
-        InputCounter nonFileCounter = new InputCounter() {
-            @Override public long count() {
-                return 0;
-            }
+            InputCounter nonFileCounter = new InputCounter() {
+                @Override public long count() {
+                    return 0;
+                }
 
-            @Override public long total() {
-                return -1;
-            }
-        };
-        InputCounter inputCounter = fin == null ? nonFileCounter
-                                                : (CountingInputStream) in;
+                @Override public long total() {
+                    return -1;
+                }
+            };
+            InputCounter inputCounter = fin == null
+                    ? nonFileCounter
+                    : (CountingInputStream) in;
 
-        try (BufferedWriter bwtr = new BufferedWriter(new OutputStreamWriter(out, UTF_8));
-             BufferedReader brdr = new BufferedReader(new InputStreamReader(in, UTF_8))) {
             process(brdr, bwtr, inputCounter, optset);
         }
     }
diff --git a/func/pom.xml b/func/pom.xml
index 670e5b4..2925c0b 100644
--- a/func/pom.xml
+++ b/func/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <artifactId>beam</artifactId>
         <groupId>uk.ac.ebi.beam</groupId>
-        <version>1.3.3</version>
+        <version>1.3.5</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
@@ -16,13 +16,11 @@
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
-            <version>4.11</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
-            <artifactId>mockito-all</artifactId>
-            <version>1.9.5</version>
+            <artifactId>mockito-core</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/func/src/main/java/uk/ac/ebi/beam/Functions.java b/func/src/main/java/uk/ac/ebi/beam/Functions.java
index 7052351..0feb2fe 100644
--- a/func/src/main/java/uk/ac/ebi/beam/Functions.java
+++ b/func/src/main/java/uk/ac/ebi/beam/Functions.java
@@ -32,6 +32,8 @@ public final class Functions {
     
     private static final AddDirectionalLabels adl = new AddDirectionalLabels();
 
+    private static Random rand = new Random();
+
     /// non-instantiable
     private Functions() {
     }
@@ -164,14 +166,28 @@ public final class Functions {
         return g;
     }
 
-    private static int[] random(int n) {
+    /**
+     * Generate a random permutation.
+     * @param n size of the permutation
+     * @param rnd random number generator
+     * @return the permutation
+     */
+    private static int[] random(int n, Random rnd) {
         int[] p = ident(n);
-        Random rnd = new Random();
         for (int i = n; i > 1; i--)
             swap(p, i - 1, rnd.nextInt(i));
         return p;
     }
 
+    /**
+     * Generate a random permutation using a shared RNG instance. The method is synchronized
+     * @param n size of the permutation
+     * @return the permutation
+     */
+    private synchronized static int[] random(int n) {
+      return random(n, rand);
+    }
+
     private static int[] reverse(int n) {
         int[] p = new int[n];
         for (int i = 0; i < n; i++)
diff --git a/func/src/main/java/uk/ac/ebi/beam/NormaliseDirectionalLabels.java b/func/src/main/java/uk/ac/ebi/beam/NormaliseDirectionalLabels.java
index 8b53630..625f5a6 100644
--- a/func/src/main/java/uk/ac/ebi/beam/NormaliseDirectionalLabels.java
+++ b/func/src/main/java/uk/ac/ebi/beam/NormaliseDirectionalLabels.java
@@ -179,6 +179,8 @@ final class NormaliseDirectionalLabels
         }
 
         private void flip(Edge first, int u, BitSet dbAtoms) {
+            if (first == null)
+                return;
             if (ordering[first.other(u)] < ordering[u]) {
                 if (first.bond(u) == Bond.UP)
                     invertExistingDirectionalLabels(g,
diff --git a/pom.xml b/pom.xml
index e114646..cf4517e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
     <description>SMILES parsing and generation library for cheminformatics</description>
     <url>http://www.github.com/johnmay/beam/</url>
     <packaging>pom</packaging>
-    <version>1.3.3</version>
+    <version>1.3.5</version>
     <modules>
         <module>core</module>
         <module>func</module>
@@ -34,6 +34,8 @@
     </distributionManagement>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <sonar.organization>johnmay</sonar.organization>
+        <sonar.host.url>https://sonarcloud.io</sonar.host.url>
     </properties>
     <licenses>
         <license>
@@ -46,14 +48,43 @@
     </prerequisites>
     <developers>
         <developer>
-            <name>John May</name>
+            <name>John Mayfield (né May)</name>
             <email>https://github.com/johnmay</email>
             <url>http://www.github.com/johnmay/</url>
-            <organization>EMBL-EBI</organization>
-            <organizationUrl>http://ebi.ac.uk</organizationUrl>
+            <organization>NextMove Software</organization>
+            <organizationUrl>https://www.nextmovesoftware.com</organizationUrl>
             <timezone>GMT</timezone>
         </developer>
     </developers>
+    <dependencyManagement>
+      <dependencies>
+        <dependency>
+          <groupId>junit</groupId>
+          <artifactId>junit</artifactId>
+          <version>4.13.2</version>
+        </dependency>
+        <dependency>
+          <groupId>org.hamcrest</groupId>
+          <artifactId>hamcrest-core</artifactId>
+          <version>2.2</version>
+        </dependency>
+        <dependency>
+          <groupId>org.hamcrest</groupId>
+          <artifactId>hamcrest-all</artifactId>
+          <version>2.2</version>
+        </dependency>
+        <dependency>
+          <groupId>org.hamcrest</groupId>
+          <artifactId>hamcrest</artifactId>
+          <version>2.2</version>
+        </dependency>
+        <dependency>
+          <groupId>org.mockito</groupId>
+          <artifactId>mockito-core</artifactId>
+          <version>4.11.0</version>
+        </dependency>
+      </dependencies>
+    </dependencyManagement>
     <profiles>
         <profile>
             <id>ossrh</id>
@@ -63,7 +94,7 @@
                         <groupId>org.sonatype.plugins</groupId>
                         <artifactId>nexus-staging-maven-plugin</artifactId>
                         <version>1.6.3</version>
-                        <extensions>true</extensions>
+                        <extensions>false</extensions>
                         <configuration>
                             <serverId>ossrh</serverId>
                             <nexusUrl>https://oss.sonatype.org/</nexusUrl>
@@ -113,6 +144,32 @@
                 </plugins>
             </build>
         </profile>
+        <profile>
+            <id>coverage</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.jacoco</groupId>
+                        <artifactId>jacoco-maven-plugin</artifactId>
+                        <version>0.8.5</version>
+                        <executions>
+                            <execution>
+                                <id>prepare-agent</id>
+                                <goals>
+                                    <goal>prepare-agent</goal>
+                                </goals>
+                            </execution>
+                            <execution>
+                                <id>report</id>
+                                <goals>
+                                    <goal>report</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
     </profiles>
     <build>
         <plugins>
@@ -121,8 +178,8 @@
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>3.6.1</version>
                 <configuration>
-                    <source>1.7</source>
-                    <target>1.7</target>
+                    <source>1.8</source>
+                    <target>1.8</target>
                 </configuration>
             </plugin>
         </plugins>

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-core/1.3.5/beam-core-1.3.5.pom
-rw-r--r--  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-exec/1.3.5/beam-exec-1.3.5.pom
-rw-r--r--  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-func/1.3.5/beam-func-1.3.5.pom
-rw-r--r--  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam/1.3.5/beam-1.3.5.pom
lrwxrwxrwx  root/root   /usr/share/java/beam-core-1.3.5.jar -> beam-core.jar
lrwxrwxrwx  root/root   /usr/share/java/beam-exec-1.3.5.jar -> beam-exec.jar
lrwxrwxrwx  root/root   /usr/share/java/beam-func-1.3.5.jar -> beam-func.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-core/1.3.5/beam-core-1.3.5.jar -> ../../../../../../../java/beam-core.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-exec/1.3.5/beam-exec-1.3.5.jar -> ../../../../../../../java/beam-exec.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-func/1.3.5/beam-func-1.3.5.jar -> ../../../../../../../java/beam-func.jar

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-core/1.3.3/beam-core-1.3.3.pom
-rw-r--r--  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-exec/1.3.3/beam-exec-1.3.3.pom
-rw-r--r--  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-func/1.3.3/beam-func-1.3.3.pom
-rw-r--r--  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam/1.3.3/beam-1.3.3.pom
lrwxrwxrwx  root/root   /usr/share/java/beam-core-1.3.3.jar -> beam-core.jar
lrwxrwxrwx  root/root   /usr/share/java/beam-exec-1.3.3.jar -> beam-exec.jar
lrwxrwxrwx  root/root   /usr/share/java/beam-func-1.3.3.jar -> beam-func.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-core/1.3.3/beam-core-1.3.3.jar -> ../../../../../../../java/beam-core.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-exec/1.3.3/beam-exec-1.3.3.jar -> ../../../../../../../java/beam-exec.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/uk/ac/ebi/beam/beam-func/1.3.3/beam-func-1.3.3.jar -> ../../../../../../../java/beam-func.jar

No differences were encountered in the control files

More details

Full run details