Skip to content

Commit 04f5d99

Browse files
committed
Add CDK adapter layer for toolkit-agnostic graph model
New cdk/ package with 6 files implementing graph model interfaces: - CDKToolkit: ChemToolkit implementation (parsing, SMILES, MCS, aromaticity) - CDKAtomNode: AtomNode wrapping CDK IAtom - CDKBondEdge: BondEdge wrapping CDK IBond - CDKMolecularGraph: MolecularGraph wrapping CDK IAtomContainer - CDKReactionGraph: ReactionGraph wrapping CDK IReaction - CDKAdapter: bidirectional conversion bridge (fromCDK/toCDK) Users can now write adapters for RDKit, OpenBabel, etc. by implementing AtomNode, BondEdge, MolecularGraph, ReactionGraph, and ChemToolkit. Co-Authored-By: Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>
1 parent 97e82f2 commit 04f5d99

6 files changed

Lines changed: 821 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (C) 2007-2026 Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>.
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17+
* MA 02110-1301 USA
18+
*/
19+
package com.bioinceptionlabs.reactionblast.cdk;
20+
21+
import org.openscience.cdk.interfaces.IAtom;
22+
import org.openscience.cdk.interfaces.IAtomContainer;
23+
import org.openscience.cdk.interfaces.IBond;
24+
import org.openscience.cdk.interfaces.IReaction;
25+
26+
import com.bioinceptionlabs.reactionblast.model.AtomNode;
27+
import com.bioinceptionlabs.reactionblast.model.BondEdge;
28+
import com.bioinceptionlabs.reactionblast.model.MolecularGraph;
29+
import com.bioinceptionlabs.reactionblast.model.ReactionGraph;
30+
31+
/**
32+
* Bidirectional conversion between CDK types and graph model types.
33+
* Use during the Strangler Fig migration: existing CDK code wraps/unwraps
34+
* through this adapter as it's gradually refactored to use graph model types.
35+
*
36+
* @author Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>
37+
*/
38+
public final class CDKAdapter {
39+
40+
private CDKAdapter() {}
41+
42+
// ---- CDK → Graph Model ----
43+
44+
public static MolecularGraph fromCDK(IAtomContainer mol) {
45+
return new CDKMolecularGraph(mol);
46+
}
47+
48+
public static ReactionGraph fromCDK(IReaction rxn) {
49+
return new CDKReactionGraph(rxn);
50+
}
51+
52+
public static AtomNode fromCDK(IAtom atom) {
53+
return new CDKAtomNode(atom);
54+
}
55+
56+
public static BondEdge fromCDK(IBond bond) {
57+
return new CDKBondEdge(bond);
58+
}
59+
60+
// ---- Graph Model → CDK ----
61+
62+
public static IAtomContainer toCDK(MolecularGraph graph) {
63+
if (graph instanceof CDKMolecularGraph) {
64+
return ((CDKMolecularGraph) graph).getCDKContainer();
65+
}
66+
throw new IllegalArgumentException(
67+
"Cannot convert non-CDK MolecularGraph to IAtomContainer. "
68+
+ "Use CDKToolkit for CDK-based operations.");
69+
}
70+
71+
public static IReaction toCDK(ReactionGraph rxn) {
72+
if (rxn instanceof CDKReactionGraph) {
73+
return ((CDKReactionGraph) rxn).getCDKReaction();
74+
}
75+
throw new IllegalArgumentException(
76+
"Cannot convert non-CDK ReactionGraph to IReaction. "
77+
+ "Use CDKToolkit for CDK-based operations.");
78+
}
79+
80+
public static IAtom toCDK(AtomNode node) {
81+
if (node instanceof CDKAtomNode) {
82+
return ((CDKAtomNode) node).getCDKAtom();
83+
}
84+
throw new IllegalArgumentException(
85+
"Cannot convert non-CDK AtomNode to IAtom.");
86+
}
87+
88+
public static IBond toCDK(BondEdge edge) {
89+
if (edge instanceof CDKBondEdge) {
90+
return ((CDKBondEdge) edge).getCDKBond();
91+
}
92+
throw new IllegalArgumentException(
93+
"Cannot convert non-CDK BondEdge to IBond.");
94+
}
95+
96+
// ---- Type checking ----
97+
98+
public static boolean isCDK(MolecularGraph graph) {
99+
return graph instanceof CDKMolecularGraph;
100+
}
101+
102+
public static boolean isCDK(ReactionGraph rxn) {
103+
return rxn instanceof CDKReactionGraph;
104+
}
105+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (C) 2007-2026 Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>.
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17+
* MA 02110-1301 USA
18+
*/
19+
package com.bioinceptionlabs.reactionblast.cdk;
20+
21+
import org.openscience.cdk.interfaces.IAtom;
22+
import com.bioinceptionlabs.reactionblast.model.AtomNode;
23+
24+
/**
25+
* CDK adapter for AtomNode. Wraps a CDK IAtom as a graph node.
26+
*
27+
* @author Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>
28+
*/
29+
public class CDKAtomNode implements AtomNode {
30+
31+
private final IAtom cdkAtom;
32+
33+
public CDKAtomNode(IAtom cdkAtom) {
34+
if (cdkAtom == null) throw new IllegalArgumentException("CDK atom cannot be null");
35+
this.cdkAtom = cdkAtom;
36+
}
37+
38+
public IAtom getCDKAtom() {
39+
return cdkAtom;
40+
}
41+
42+
@Override public String getSymbol() { return cdkAtom.getSymbol(); }
43+
@Override public int getAtomicNumber() { return cdkAtom.getAtomicNumber() != null ? cdkAtom.getAtomicNumber() : 0; }
44+
@Override public Integer getFormalCharge() { return cdkAtom.getFormalCharge(); }
45+
@Override public Integer getMassNumber() { return cdkAtom.getMassNumber(); }
46+
@Override public boolean isAromatic() { return cdkAtom.isAromatic(); }
47+
@Override public void setAromatic(boolean aromatic) { cdkAtom.setIsAromatic(aromatic); }
48+
@Override public Integer getImplicitHydrogenCount() { return cdkAtom.getImplicitHydrogenCount(); }
49+
@Override public String getId() { return cdkAtom.getID(); }
50+
@Override public void setId(String id) { cdkAtom.setID(id); }
51+
@Override public Object getProperty(String key) { return cdkAtom.getProperty(key); }
52+
@Override public void setProperty(String key, Object value) { cdkAtom.setProperty(key, value); }
53+
@Override public boolean getFlag(int flag) { return cdkAtom.getFlag(flag); }
54+
@Override public void setFlag(int flag, boolean value) { cdkAtom.setFlag(flag, value); }
55+
56+
@Override
57+
public boolean equals(Object o) {
58+
if (this == o) return true;
59+
if (o instanceof CDKAtomNode) return cdkAtom == ((CDKAtomNode) o).cdkAtom;
60+
return false;
61+
}
62+
63+
@Override
64+
public int hashCode() {
65+
return System.identityHashCode(cdkAtom);
66+
}
67+
68+
@Override
69+
public String toString() {
70+
return getSymbol() + (getId() != null ? ":" + getId() : "");
71+
}
72+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright (C) 2007-2026 Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>.
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17+
* MA 02110-1301 USA
18+
*/
19+
package com.bioinceptionlabs.reactionblast.cdk;
20+
21+
import org.openscience.cdk.interfaces.IBond;
22+
import com.bioinceptionlabs.reactionblast.model.AtomNode;
23+
import com.bioinceptionlabs.reactionblast.model.BondEdge;
24+
25+
/**
26+
* CDK adapter for BondEdge. Wraps a CDK IBond as a graph edge.
27+
*
28+
* @author Syed Asad Rahman <asad.rahman@bioinceptionlabs.com>
29+
*/
30+
public class CDKBondEdge implements BondEdge {
31+
32+
private final IBond cdkBond;
33+
private final CDKAtomNode source;
34+
private final CDKAtomNode target;
35+
36+
public CDKBondEdge(IBond cdkBond) {
37+
if (cdkBond == null) throw new IllegalArgumentException("CDK bond cannot be null");
38+
this.cdkBond = cdkBond;
39+
this.source = new CDKAtomNode(cdkBond.getBegin());
40+
this.target = new CDKAtomNode(cdkBond.getEnd());
41+
}
42+
43+
public IBond getCDKBond() {
44+
return cdkBond;
45+
}
46+
47+
@Override public AtomNode getSource() { return source; }
48+
@Override public AtomNode getTarget() { return target; }
49+
50+
@Override
51+
public BondOrder getOrder() {
52+
if (cdkBond.getOrder() == null) return BondOrder.UNSET;
53+
switch (cdkBond.getOrder()) {
54+
case SINGLE: return BondOrder.SINGLE;
55+
case DOUBLE: return BondOrder.DOUBLE;
56+
case TRIPLE: return BondOrder.TRIPLE;
57+
case QUADRUPLE: return BondOrder.QUADRUPLE;
58+
default: return BondOrder.UNSET;
59+
}
60+
}
61+
62+
@Override
63+
public void setOrder(BondOrder order) {
64+
switch (order) {
65+
case SINGLE: cdkBond.setOrder(IBond.Order.SINGLE); break;
66+
case DOUBLE: cdkBond.setOrder(IBond.Order.DOUBLE); break;
67+
case TRIPLE: cdkBond.setOrder(IBond.Order.TRIPLE); break;
68+
case QUADRUPLE: cdkBond.setOrder(IBond.Order.QUADRUPLE); break;
69+
default: cdkBond.setOrder(IBond.Order.UNSET); break;
70+
}
71+
}
72+
73+
@Override public boolean isAromatic() { return cdkBond.isAromatic(); }
74+
@Override public void setAromatic(boolean aromatic) { cdkBond.setIsAromatic(aromatic); }
75+
76+
@Override
77+
public boolean connects(AtomNode atom) {
78+
if (atom instanceof CDKAtomNode) {
79+
return cdkBond.contains(((CDKAtomNode) atom).getCDKAtom());
80+
}
81+
return false;
82+
}
83+
84+
@Override
85+
public boolean equals(Object o) {
86+
if (this == o) return true;
87+
if (o instanceof CDKBondEdge) return cdkBond == ((CDKBondEdge) o).cdkBond;
88+
return false;
89+
}
90+
91+
@Override
92+
public int hashCode() {
93+
return System.identityHashCode(cdkBond);
94+
}
95+
}

0 commit comments

Comments
 (0)