[Git][java-team/libbeam-java][upstream] New upstream version 1.4
Andrius Merkys (@merkys)
gitlab at salsa.debian.org
Wed Sep 9 06:05:28 BST 2026
Andrius Merkys pushed to branch upstream at Debian Java Maintainers / libbeam-java
Commits:
aca58b0f by Andrius Merkys at 2026-09-08T09:30:15-04:00
New upstream version 1.4
- - - - -
14 changed files:
- .github/workflows/build.yml
- core/pom.xml
- core/src/main/java/uk/ac/ebi/beam/Graph.java
- + core/src/main/java/uk/ac/ebi/beam/Mode.java
- core/src/main/java/uk/ac/ebi/beam/Parser.java
- core/src/test/java/uk/ac/ebi/beam/GraphTest.java
- core/src/test/java/uk/ac/ebi/beam/ParserTest.java
- core/src/test/java/uk/ac/ebi/beam/ParsingAtomClassTest.java
- core/src/test/java/uk/ac/ebi/beam/ParsingBracketAtomTest.java
- + core/src/test/java/uk/ac/ebi/beam/RelaxedParsingTest.java
- exec/pom.xml
- func/pom.xml
- func/src/main/java/uk/ac/ebi/beam/NormaliseDirectionalLabels.java
- pom.xml
Changes:
=====================================
.github/workflows/build.yml
=====================================
@@ -7,30 +7,29 @@ on:
types: [opened, synchronize, reopened]
jobs:
build:
- name: Build
+ name: Build sonarcloud
runs-on: ubuntu-latest
+ permissions:
+ contents: read
steps:
- - uses: actions/checkout at v2
+ - uses: actions/checkout at v6
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- - name: Set up JDK 11
- uses: actions/setup-java at v1
+ - name: Set up JDK 17
+ uses: actions/setup-java at v5
with:
- java-version: 11
+ distribution: 'temurin'
+ java-version: 17
+ cache: maven
- name: Cache SonarCloud packages
- uses: actions/cache at v1
+ uses: actions/cache at v5
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- - name: Cache Maven packages
- uses: actions/cache at 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 }}
+ MAVEN_OPTS: -Xss16m -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.http.retryHandler.class=standard -Dmaven.wagon.http.retryHandler.count=3
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=johnmay_beam -Pcoverage
\ No newline at end of file
=====================================
core/pom.xml
=====================================
@@ -5,7 +5,7 @@
<parent>
<artifactId>beam</artifactId>
<groupId>uk.ac.ebi.beam</groupId>
- <version>1.3.9</version>
+ <version>1.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
=====================================
core/src/main/java/uk/ac/ebi/beam/Graph.java
=====================================
@@ -113,7 +113,8 @@ public final class Graph {
this.degrees = new int[order];
this.edges = new Edge[order][];
this.topologies = Arrays.copyOf(org.topologies, org.topologies.length);
-
+ this.title = org.title;
+
for (int u = 0; u < order; u++) {
final int deg = org.degrees[u];
this.edges[u] = new Edge[deg];
@@ -428,7 +429,7 @@ public final class Graph {
InvalidSmilesException {
if (smi == null)
throw new NullPointerException("no SMILES provided");
- Parser parser = new Parser(CharBuffer.fromString(smi), false);
+ Parser parser = new Parser(CharBuffer.fromString(smi), Mode.Default);
for (String warn : parser.getWarnings()) {
for (String line : warn.split("\n"))
System.err.println("SMILES Warning: " + line);
@@ -436,10 +437,10 @@ public final class Graph {
return parser.molecule();
}
- public static Graph parse(String smi, boolean strict, Set<String> warnings) throws InvalidSmilesException {
+ public static Graph parse(String smi, Mode mode, Set<String> warnings) throws InvalidSmilesException {
if (smi == null)
throw new NullPointerException("no SMILES provided");
- Parser parser = new Parser(CharBuffer.fromString(smi), strict);
+ Parser parser = new Parser(CharBuffer.fromString(smi), mode);
warnings.addAll(parser.getWarnings());
return parser.molecule();
}
=====================================
core/src/main/java/uk/ac/ebi/beam/Mode.java
=====================================
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2026, John Mayfield
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those
+ * of the authors and should not be interpreted as representing official policies,
+ * either expressed or implied, of the FreeBSD Project.
+ */
+package uk.ac.ebi.beam;
+
+/**
+ * Parsing mode, this sets how strict the SMILES parser is.
+ */
+public enum Mode {
+ /* Fail (exception) on errors and warnings. */
+ Strict,
+ /* Fail (exception) on errors only. */
+ Default,
+ /* Treat recoverable errors as warnings. */
+ Relaxed
+}
=====================================
core/src/main/java/uk/ac/ebi/beam/Parser.java
=====================================
@@ -29,6 +29,7 @@
package uk.ac.ebi.beam;
+import javax.swing.plaf.nimbus.AbstractRegionPainter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@@ -103,9 +104,9 @@ final class Parser {
private int openRings = 0;
/**
- * Strict parsing.
+ * Parsing mode
*/
- private final boolean strict;
+ private Mode mode;
private BitSet checkDirectionalBonds = new BitSet();
@@ -120,15 +121,16 @@ final class Parser {
* Create a new parser for the specified buffer.
*
* @param buffer character buffer holding a SMILES string
+ * @param mode the mode
* @throws InvalidSmilesException thrown if the SMILES could not be parsed
*/
- Parser(CharBuffer buffer, boolean strict) throws InvalidSmilesException {
- this.strict = strict;
+ Parser(CharBuffer buffer, Mode mode) throws InvalidSmilesException {
+ this.mode = mode;
g = new Graph(1 + (2 * (buffer.length() / 3)));
readSmiles(buffer);
- if (openRings > 0)
+ if (openRings > 0 && mode != Mode.Relaxed)
throw new InvalidSmilesException("Unclosed ring detected, SMILES may be truncated:", buffer);
- if (stack.size() > 1)
+ if (stack.size() > 1 && mode != Mode.Relaxed)
throw new InvalidSmilesException("Unclosed branch detected, SMILES may be truncated:", buffer);
start.add(0); // always include first vertex as start
if (g.getFlags(Graph.HAS_STRO) != 0) {
@@ -169,7 +171,7 @@ final class Parser {
* @throws InvalidSmilesException thrown if the SMILES could not be parsed
*/
Parser(String str) throws InvalidSmilesException {
- this(CharBuffer.fromString(str), false);
+ this(CharBuffer.fromString(str), Mode.Default);
}
/**
@@ -181,7 +183,7 @@ final class Parser {
* @throws InvalidSmilesException
*/
static Graph strict(String str) throws InvalidSmilesException {
- return new Parser(CharBuffer.fromString(str), true).molecule();
+ return new Parser(CharBuffer.fromString(str), Mode.Strict).molecule();
}
/**
@@ -194,8 +196,8 @@ final class Parser {
* @return a graph created with the loose parser
* @throws InvalidSmilesException
*/
- static Graph losse(String str) throws InvalidSmilesException {
- return new Parser(CharBuffer.fromString(str), false).molecule();
+ static Graph relaxed(String str) throws InvalidSmilesException {
+ return new Parser(CharBuffer.fromString(str), Mode.Relaxed).molecule();
}
/**
@@ -272,7 +274,7 @@ final class Parser {
String errorPos = InvalidSmilesException.display(buffer,
offset1 - buffer.length(),
offset2 - buffer.length());
- if (strict)
+ if (mode == Mode.Strict)
throw new InvalidSmilesException("Ignored invalid Cis/Trans specification: " + errorPos);
else
warnings.add("Ignored invalid Cis/Trans specification: " + errorPos);
@@ -289,7 +291,7 @@ final class Parser {
String errorPos = InvalidSmilesException.display(buffer,
offset1 - buffer.length(),
offset2 - buffer.length());
- if (strict)
+ if (mode == Mode.Strict)
throw new InvalidSmilesException("Ignored invalid Cis/Trans specification: " + errorPos);
else
warnings.add("Ignored invalid Cis/Trans specification: " + errorPos);
@@ -359,8 +361,10 @@ final class Parser {
boolean begh = g.implHCount(beg) == 1;
boolean endh = g.implHCount(end) == 1;
List<Edge> begEdges = new ArrayList<>(getLocalEdges(beg));
- if (begh)
+
+ if (begh || g.degree(beg) == 2)
begEdges.add(start.contains(beg) ? 0 : 1, null);
+
for (Edge bEdge : begEdges) {
if (bEdge == null) {
carriers[i++] = beg;
@@ -370,8 +374,10 @@ final class Parser {
if (bEdge.bond() == Bond.DOUBLE) {
// neighbors next to end
List<Edge> endEdges = new ArrayList<>(getLocalEdges(end));
- if (endh)
+
+ if (endh || g.degree(end) == 2)
endEdges.add(1, null);
+
for (Edge eEdge : endEdges) {
if (eEdge == null)
carriers[i++] = end;
@@ -387,6 +393,36 @@ final class Parser {
return carriers;
}
+
+ private void warning(String mesg) throws InvalidSmilesException {
+ if (mode == Mode.Strict)
+ throw new InvalidSmilesException(mesg);
+ else
+ warnings.add(mesg);
+ }
+
+ private void warning(String mesg, CharBuffer input) throws InvalidSmilesException {
+ if (mode == Mode.Strict)
+ throw new InvalidSmilesException(mesg, input);
+ else
+ warnings.add(mesg);
+ }
+
+ private void error(String mesg) throws InvalidSmilesException {
+ if (mode != Mode.Relaxed)
+ throw new InvalidSmilesException(mesg);
+ else
+ warnings.add(mesg);
+ }
+
+ private void error(String mesg, CharBuffer input) throws InvalidSmilesException {
+ if (mode != Mode.Relaxed)
+ throw new InvalidSmilesException(mesg, input, input.position());
+ else
+ warnings.add(mesg);
+ }
+
+
/**
* Add a topology for vertex 'u' with configuration 'c'. If the atom 'u' was
* involved in a ring closure the local arrangement is used instead of the
@@ -411,10 +447,7 @@ final class Parser {
} else if (c.type() == Configuration.Type.ExtendedTetrahedral) {
g.addFlags(Graph.HAS_EXT_STRO);
if ((us = getAlleneCarriers(u)) == null) {
- if (strict)
- throw new InvalidSmilesException("Invalid Allene stereo");
- else
- warnings.add("Ignored invalid Allene stereochemistry");
+ warning("Invalid Allene stereo");
return;
}
} else if (input.type() == Configuration.Type.SquarePlanar) {
@@ -425,24 +458,15 @@ final class Parser {
us = insertMultipleImplicitRefs(u, us, 6);
} 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");
+ warning("SquarePlanar without 4 explicit neighbours");
return;
} else if (c.type() == Configuration.Type.TrigonalBipyramidal &&
us.length != 5) {
- if (strict)
- throw new InvalidSmilesException("TrigonalBipyramidal without 5 explicit neighbours");
- else
- warnings.add("SquarePlanar without 5 explicit neighbours");
+ warning("TrigonalBipyramidal without 5 explicit neighbours");
return;
} else if (c.type() == Configuration.Type.Octahedral &&
us.length != 6) {
- if (strict)
- throw new InvalidSmilesException("Octahedral without 6 explicit neighbours");
- else
- warnings.add("SquarePlanar without 6 explicit neighbours");
+ warning("Octahedral without 6 explicit neighbours");
return;
}
g.addTopology(Topology.create(u, us, es, c));
@@ -468,32 +492,23 @@ final class Parser {
us = insertMultipleImplicitRefs(u, us, 6);
} 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");
+ warning("SquarePlanar without 4 explicit neighbours");
return;
} else if (c.type() == Configuration.Type.TrigonalBipyramidal &&
us.length != 5) {
- if (strict)
- throw new InvalidSmilesException("TrigonalBipyramidal without 5 explicit neighbours");
- else
- warnings.add("SquarePlanar without 5 explicit neighbours");
+ warning("TrigonalBipyramidal without 5 explicit neighbours");
return;
} else if (c.type() == Configuration.Type.Octahedral &&
us.length != 6) {
- if (strict)
- throw new InvalidSmilesException("Octahedral without 6 explicit neighbours");
- else
- warnings.add("SquarePlanar without 6 explicit neighbours");
+ warning("Octahedral without 6 explicit neighbours");
return;
}
+
g.addTopology(Topology.create(u, us, es, c));
}
}
- private int[] insertThImplicitRef(int u, int[] vs) throws
- InvalidSmilesException {
+ private int[] insertThImplicitRef(int u, int[] vs) throws InvalidSmilesException {
if (vs.length == 4)
return vs;
if (vs.length != 3)
@@ -529,7 +544,7 @@ final class Parser {
if (vs.length == 3)
return vs;
if (vs.length != 2)
- throw new InvalidSmilesException("Invaid number of verticies for DB1/DB2 stereo chemistry");
+ throw new InvalidSmilesException("Invalid number of vertices for DB1/DB2 stereo chemistry");
if (start.contains(u))
return new int[]{u, vs[0], vs[1]};
else
@@ -651,21 +666,15 @@ final class Parser {
// says it's possible. The D and T here are automatic converted
// to [2H] and [3H].
case 'H':
- if (strict)
- throw new InvalidSmilesException("hydrogens should be specified in square brackets - '[H]'",
- buffer);
+ warning("hydrogens should be specified in square brackets - '[H]'", buffer);
addAtom(AtomImpl.EXPLICIT_HYDROGEN, buffer);
break;
case 'D':
- if (strict)
- throw new InvalidSmilesException("deuterium should be specified as a hydrogen isotope - '[2H]'",
- buffer);
+ warning("deuterium should be specified as a hydrogen isotope - '[2H]'", buffer);
addAtom(AtomImpl.DEUTERIUM, buffer);
break;
case 'T':
- if (strict)
- throw new InvalidSmilesException("tritium should be specified as a hydrogen isotope - '[3H]'",
- buffer);
+ warning("tritium should be specified as a hydrogen isotope - '[3H]'", buffer);
addAtom(AtomImpl.TRITIUM, buffer);
break;
@@ -689,78 +698,83 @@ final class Parser {
break;
case '%':
int num = buffer.getNumber(2);
- if (num < 0)
- throw new InvalidSmilesException("a number (<digit>+) must follow '%':", buffer);
- if (strict && num < 10)
- throw new InvalidSmilesException("two digits must follow '%'", buffer);
+ if (num < 0) {
+ error("a number (<digit>+) must follow '%':", buffer);
+ continue;
+ }
+ if (num < 10) {
+ warning("two digits must follow '%'", buffer);
+ }
ring(num, buffer);
lastBondPos = buffer.position();
break;
// bond/dot
case '-':
- if (bond != Bond.IMPLICIT)
- throw new InvalidSmilesException("Multiple bonds specified:", buffer);
+ if (bond != Bond.IMPLICIT || stack.empty())
+ error("Invalid bond:", buffer);
bond = Bond.SINGLE;
lastBondPos = buffer.position();
break;
case '=':
- if (bond != Bond.IMPLICIT)
- throw new InvalidSmilesException("Multiple bonds specified:", buffer);
+ if (bond != Bond.IMPLICIT || stack.empty())
+ error("Invalid bond:", buffer);
bond = Bond.DOUBLE;
lastBondPos = buffer.position();
break;
case '#':
- if (bond != Bond.IMPLICIT)
- throw new InvalidSmilesException("Multiple bonds specified:", buffer);
+ if (bond != Bond.IMPLICIT || stack.empty())
+ error("Invalid bond:", buffer);
bond = Bond.TRIPLE;
lastBondPos = buffer.position();
break;
case '$':
- if (bond != Bond.IMPLICIT)
- throw new InvalidSmilesException("Multiple bonds specified:", buffer);
+ if (bond != Bond.IMPLICIT || stack.empty())
+ error("Invalid bond:", buffer);
bond = Bond.QUADRUPLE;
lastBondPos = buffer.position();
break;
case ':':
- if (bond != Bond.IMPLICIT)
- throw new InvalidSmilesException("Multiple bonds specified:", buffer);
+ if (bond != Bond.IMPLICIT || stack.empty())
+ error("Invalid bond:", buffer);
g.addFlags(Graph.HAS_AROM);
bond = Bond.AROMATIC;
lastBondPos = buffer.position();
break;
case '/':
- if (bond != Bond.IMPLICIT)
- throw new InvalidSmilesException("Multiple bonds specified:", buffer);
+ if (bond != Bond.IMPLICIT || stack.empty())
+ error("Invalid bond:", buffer);
bond = Bond.UP;
lastBondPos = buffer.position();
g.addFlags(Graph.HAS_BND_STRO);
break;
case '\\':
// we allow C\\C=C/C since it could be an escaping error
- if (bond != Bond.IMPLICIT && bond != Bond.DOWN)
- throw new InvalidSmilesException("Multiple bonds specified:", buffer);
+ if (bond != Bond.IMPLICIT && bond != Bond.DOWN || stack.empty())
+ error("Invalid bond:", buffer);
bond = Bond.DOWN;
lastBondPos = buffer.position();
g.addFlags(Graph.HAS_BND_STRO);
break;
case '.':
if (bond != Bond.IMPLICIT)
- throw new InvalidSmilesException("Bond specified before disconnection:", buffer);
+ error("Invalid disconnection:", buffer);
bond = Bond.DOT;
break;
// branching
case '(':
- if (stack.empty())
- throw new InvalidSmilesException("Cannot open branch at this position, SMILES may be truncated:",
- buffer);
+ if (stack.empty()) {
+ error("Cannot open branch at this position, SMILES may be truncated:", buffer);
+ continue;
+ }
stack.push(stack.peek());
break;
case ')':
- if (stack.size() < 2)
- throw new InvalidSmilesException("Closing of an unopened branch, SMILES may be truncated:",
- buffer);
+ if (stack.size() < 2) {
+ error("Closing of an unopened branch, SMILES may be truncated:", buffer);
+ continue;
+ }
stack.pop();
break;
@@ -782,6 +796,11 @@ final class Parser {
case '\r':
return;
+ case ']':
+ case '@':
+ case '+':
+ error("unexpected character:", buffer);
+ continue;
default:
throw new InvalidSmilesException("unexpected character:", buffer);
}
@@ -809,8 +828,10 @@ final class Parser {
boolean arbitraryLabel = false;
- if (!buffer.hasRemaining())
- throw new InvalidSmilesException("Unclosed bracket atom, SMILES may be truncated", buffer);
+ if (!buffer.hasRemaining()) {
+ error("Unclosed bracket atom, SMILES may be truncated", buffer);
+ return AtomImpl.AliphaticSubset.Any;
+ }
final int isotope = buffer.getNumber();
final boolean aromatic = buffer.next() >= 'a' && buffer.next() <= 'z';
@@ -818,15 +839,15 @@ final class Parser {
if (element == Element.Unknown)
hasAstrix = true;
- if (strict && element == null)
- throw new InvalidSmilesException("unrecognised element symbol, SMILES may be truncated: ", buffer);
+ if (mode == Mode.Strict && element == null)
+ warning("unrecognised element symbol, SMILES may be truncated: ", buffer);
if (element != null && aromatic)
g.addFlags(Graph.HAS_AROM);
// element isn't aromatic as per the OpenSMILES specification
- if (strict && aromatic && !element.aromatic(Element.AromaticSpecification.OpenSmiles))
- throw new InvalidSmilesException("abnormal aromatic element", buffer);
+ if (mode == Mode.Strict && aromatic && !element.aromatic(Element.AromaticSpecification.OpenSmiles))
+ warning("abnormal aromatic element", buffer);
if (element == null) {
arbitraryLabel = true;
@@ -839,10 +860,12 @@ final class Parser {
int atomClass = readClass(buffer);
if (!arbitraryLabel && !buffer.getIf(']')) {
- if (strict) {
+ if (mode == Mode.Strict) {
throw InvalidSmilesException.invalidBracketAtom(buffer);
- } else {
+ } else if (buffer.hasRemaining()) {
arbitraryLabel = true;
+ } else {
+ error("Unclosed bracket atom!");
}
}
@@ -861,9 +884,7 @@ final class Parser {
end++;
}
if (depth != 0)
- throw new InvalidSmilesException("unparsable label in bracket atom",
- buffer,
- buffer.position - 1);
+ error("Unclosed bracket atom", buffer);
String label = buffer.substr(start, end);
hasAstrix = true;
return new AtomImpl.BracketAtom(label);
@@ -940,11 +961,11 @@ final class Parser {
* @see <a href="http://www.opensmiles.org/opensmiles.html#atomclass">Atom
* Class - OpenSMILES Specification</a>
*/
- static int readClass(CharBuffer buffer) throws InvalidSmilesException {
+ int readClass(CharBuffer buffer) throws InvalidSmilesException {
if (buffer.getIf(':')) {
if (buffer.nextIsDigit())
return buffer.getNumber();
- throw new InvalidSmilesException("invalid atom class, <digit>+ must follow ':'", buffer);
+ error("invalid atom class, <digit>+ must follow ':'", buffer);
}
return 0;
}
@@ -956,14 +977,14 @@ final class Parser {
* @throws InvalidSmilesException bond types did not match on ring closure
*/
private void ring(int rnum, CharBuffer buffer) throws InvalidSmilesException {
- if (bond == Bond.DOT)
- throw new InvalidSmilesException("a ring bond can not be a 'dot':",
- buffer,
- buffer.position());
- if (stack.empty())
- throw new InvalidSmilesException("No previous atom for ring open!",
- buffer,
- buffer.position());
+ if (bond == Bond.DOT) {
+ error("a ring bond can not be a 'dot':", buffer);
+ return;
+ }
+ if (stack.empty()) {
+ error("No previous atom for ring open!", buffer);
+ return;
+ }
if (rings.length <= rnum || rings[rnum] == null)
openRing(rnum, buffer);
@@ -1030,9 +1051,11 @@ final class Parser {
throw new InvalidSmilesException("Endpoints of ringbond are the same - loops are not allowed",
buffer);
- if (g.adjacent(u, v))
- throw new InvalidSmilesException("Endpoints of ringbond are already connected - multi-edges are not allowed",
- buffer);
+ if (g.adjacent(u, v)) {
+ error("Endpoints of ringbond are already connected - multi-edges are not allowed",
+ buffer);
+ return;
+ }
bond = decideBond(rbond.bond, bond.inverse(), rbond.pos, buffer);
@@ -1080,11 +1103,13 @@ final class Parser {
return b;
else if (b == Bond.IMPLICIT)
return a;
- if (strict || a.inverse() != b)
- throw new InvalidSmilesException("Ring closure bonds did not match, '" + a + "'!='" + b + "':" +
- InvalidSmilesException.display(buffer,
- pos - buffer.position,
- lastBondPos - buffer.position));
+ if (mode == Mode.Strict || a.inverse() != b) {
+ error("Ring closure bonds did not match, '" + a + "'!='" + b + "':" +
+ InvalidSmilesException.display(buffer,
+ pos - buffer.position,
+ lastBondPos - buffer.position));
+ return b; // arbitrary closure takes priority
+ }
warnings.add("Ignored invalid Cis/Trans on ring closure, should flip:" +
InvalidSmilesException.display(buffer, pos - buffer.position,
lastBondPos - buffer.position));
=====================================
core/src/test/java/uk/ac/ebi/beam/GraphTest.java
=====================================
@@ -546,6 +546,14 @@ public class GraphTest {
Assert.assertThat(g.topologyOf(3).configuration(), is(Configuration.AL2));
}
+
+ @Test public void extendedTetrahedralNitrogen() throws InvalidSmilesException {
+ Graph g = Graph.fromSmiles("CN=[C at AL1]=NC");
+ Assert.assertThat(g.topologyOf(2).configuration(), is(Configuration.AL1));
+ g = Graph.fromSmiles("CN=[C@]=NC");
+ Assert.assertThat(g.topologyOf(2).configuration(), is(Configuration.AL1));
+ }
+
@Test public void testDegenerateOctahedral1() throws Exception {
assertThat(Graph.fromSmiles("N[Co at OH1]N").toSmiles(),
containsString("N[Co at OH1]N"));
=====================================
core/src/test/java/uk/ac/ebi/beam/ParserTest.java
=====================================
@@ -108,7 +108,7 @@ public class ParserTest {
}
@Test public void hydrogen() throws IOException {
- Graph g = Parser.losse("HH");
+ Graph g = Parser.parse("HH");
assertThat(g.order(), is(2));
assertThat(g.toSmiles(), is("[H][H]"));
}
@@ -119,7 +119,7 @@ public class ParserTest {
}
@Test public void deuterium() throws IOException {
- Graph g = Parser.losse("DD");
+ Graph g = Parser.parse("DD");
assertThat(g.order(), is(2));
assertThat(g.toSmiles(), is("[2H][2H]"));
}
@@ -130,7 +130,7 @@ public class ParserTest {
}
@Test public void tritium() throws IOException {
- Graph g = Parser.losse("TT");
+ Graph g = Parser.parse("TT");
assertThat(g.order(), is(2));
assertThat(g.toSmiles(), is("[3H][3H]"));
}
@@ -148,7 +148,7 @@ public class ParserTest {
}
@Test public void tellurium() throws IOException {
- Graph g = Parser.losse("[te]");
+ Graph g = Parser.relaxed("[te]");
assertTrue(g.atom(0).aromatic());
assertThat(g.atom(0).element(), is(Element.Tellurium));
}
=====================================
core/src/test/java/uk/ac/ebi/beam/ParsingAtomClassTest.java
=====================================
@@ -80,6 +80,8 @@ public class ParsingAtomClassTest {
private void verify(String str, int atomClass) throws
InvalidSmilesException {
- assertThat(Parser.readClass(CharBuffer.fromString(str)), is(atomClass));
+ assertThat(new Parser(CharBuffer.fromString(""), Mode.Default)
+ .readClass(CharBuffer.fromString(str)),
+ is(atomClass));
}
}
=====================================
core/src/test/java/uk/ac/ebi/beam/ParsingBracketAtomTest.java
=====================================
@@ -134,7 +134,7 @@ public class ParsingBracketAtomTest {
private Atom parse(String str) throws InvalidSmilesException {
CharBuffer buffer = CharBuffer.fromString(str);
- return new Parser(buffer, false).molecule().atom(0);
+ return new Parser(buffer, Mode.Relaxed).molecule().atom(0);
}
private Atom atom(Element e) {
=====================================
core/src/test/java/uk/ac/ebi/beam/RelaxedParsingTest.java
=====================================
@@ -0,0 +1,61 @@
+package uk.ac.ebi.beam;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class RelaxedParsingTest {
+
+ public static void assertRelaxed(String exp, String smi) throws IOException {
+ Assert.assertThrows(smi + " should throw and exception when not relaxed parsing",
+ InvalidSmilesException.class, () -> {Parser.parse(smi);});
+ String act = Parser.relaxed(smi).toSmiles();
+ Assert.assertEquals(smi + " was not parsed expected", exp, act);
+ }
+
+ @Test
+ public void testRelaxedParsing_unclosedRings() throws IOException {
+ assertRelaxed("CCCC", "C1CCC");
+ assertRelaxed("CCCC", "C%CCC");
+ assertRelaxed("C.CCC", "C.1CCC");
+ assertRelaxed("CC1CCCC1", "CC1CCCC12");
+ assertRelaxed("CC1CCCC1", "CC12CCCC1");
+ assertRelaxed("CC1CCCC1", "CC12CCCC1");
+ assertRelaxed("CC", "C1C1");
+ assertRelaxed("C#1CCC1", "C=1CCC#1");
+ assertRelaxed("C=1CCC1", "C#1CCC=1");
+ }
+
+ @Test
+ public void testRelaxedParsing_unclosedBranches() throws IOException {
+ assertRelaxed("CCCC", "C(CCC");
+ assertRelaxed("CCCC", "C((CCC");
+ assertRelaxed("CCCC", "C)CCC");
+ assertRelaxed("CCCC", "C))CCC");
+ assertRelaxed("CCC(CO)O", "CCC(CO)O(");
+ }
+
+ @Test
+ public void testRelaxedParsing_bondTypes() throws IOException {
+ assertRelaxed("CC#CC", "CC-=#CC");
+ assertRelaxed("CC=CC", "CC-==CC");
+ assertRelaxed("CC=CC", "CC#=CC");
+ assertRelaxed("CC.CC", "CC..CC");
+ assertRelaxed("CC.CC", "CC..CC");
+ // assertRelaxed("CC", ".CC");
+ assertRelaxed("CC", "=CC");
+ }
+
+ @Test
+ public void testRelaxedParsing_atoms() throws IOException {
+ assertRelaxed("CC[NH2]", "CC[NH2");
+ assertRelaxed("CC[*]", "CC[NBoc");
+ assertRelaxed("CC*", "CC[");
+ assertRelaxed("CC", "]CC");
+ assertRelaxed("CC", "@]CC");
+ assertRelaxed("[H]CC", "@H]CC");
+ assertRelaxed("CC", "+]CC");
+ }
+
+}
=====================================
exec/pom.xml
=====================================
@@ -5,10 +5,10 @@
<parent>
<artifactId>beam</artifactId>
<groupId>uk.ac.ebi.beam</groupId>
- <version>1.3.9</version>
+ <version>1.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
-
+ <name>beam-exec</name>
<artifactId>beam-exec</artifactId>
<dependencies>
=====================================
func/pom.xml
=====================================
@@ -5,7 +5,7 @@
<parent>
<artifactId>beam</artifactId>
<groupId>uk.ac.ebi.beam</groupId>
- <version>1.3.9</version>
+ <version>1.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
=====================================
func/src/main/java/uk/ac/ebi/beam/NormaliseDirectionalLabels.java
=====================================
@@ -86,7 +86,7 @@ final class NormaliseDirectionalLabels
if (cmp != 0) return cmp;
int max1 = Math.max(ordering[u1], ordering[v1]);
int max2 = Math.max(ordering[u2], ordering[v2]);
- return max1 - max2;
+ return Integer.compare(max1, max2);
}
});
=====================================
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.9</version>
+ <version>1.4</version>
<modules>
<module>core</module>
<module>func</module>
@@ -24,12 +24,12 @@
</issueManagement>
<distributionManagement>
<snapshotRepository>
- <id>ossrh</id>
- <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+ <id>central</id>
+ <url>https://central.sonatype.com/repository/maven-snapshots/</url>
</snapshotRepository>
<repository>
- <id>ossrh</id>
- <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+ <id>central</id>
+ <url>https://central.sonatype.com/</url>
</repository>
</distributionManagement>
<properties>
@@ -91,14 +91,14 @@
<build>
<plugins>
<plugin>
- <groupId>org.sonatype.plugins</groupId>
- <artifactId>nexus-staging-maven-plugin</artifactId>
- <version>1.6.13</version>
+ <groupId>org.sonatype.central</groupId>
+ <artifactId>central-publishing-maven-plugin</artifactId>
+ <version>0.8.0</version>
<extensions>true</extensions>
<configuration>
- <serverId>ossrh-beam</serverId>
- <nexusUrl>https://oss.sonatype.org/</nexusUrl>
- <autoReleaseAfterClose>true</autoReleaseAfterClose>
+ <publishingServerId>central</publishingServerId>
+ <autoPublish>true</autoPublish>
+ <waitUntil>published</waitUntil>
</configuration>
</plugin>
<plugin>
@@ -159,7 +159,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
- <version>0.8.5</version>
+ <version>0.8.14</version>
<executions>
<execution>
<id>prepare-agent</id>
View it on GitLab: https://salsa.debian.org/java-team/libbeam-java/-/commit/aca58b0f1b3848e0cca2c731fe97fd3e9b2d58bb
--
View it on GitLab: https://salsa.debian.org/java-team/libbeam-java/-/commit/aca58b0f1b3848e0cca2c731fe97fd3e9b2d58bb
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20260909/4fd4a736/attachment.htm>
More information about the pkg-java-commits
mailing list