2424 */
2525package org .openscience .cdk .io ;
2626
27+ import org .openscience .cdk .AtomRef ;
2728import org .openscience .cdk .CDKConstants ;
29+ import org .openscience .cdk .config .Elements ;
2830import org .openscience .cdk .config .Isotopes ;
2931import org .openscience .cdk .exception .CDKException ;
3032import org .openscience .cdk .interfaces .IAtom ;
4446import org .openscience .cdk .io .setting .IOSetting ;
4547import org .openscience .cdk .io .setting .StringIOSetting ;
4648import org .openscience .cdk .isomorphism .matchers .Expr ;
49+ import org .openscience .cdk .isomorphism .matchers .IQueryAtom ;
50+ import org .openscience .cdk .isomorphism .matchers .QueryAtom ;
4751import org .openscience .cdk .isomorphism .matchers .QueryBond ;
4852import org .openscience .cdk .sgroup .Sgroup ;
4953import org .openscience .cdk .sgroup .SgroupBracket ;
6367import java .nio .charset .StandardCharsets ;
6468import java .text .NumberFormat ;
6569import java .text .SimpleDateFormat ;
66- import java .util .ArrayList ;
67- import java .util .Arrays ;
68- import java .util .Collection ;
69- import java .util .HashMap ;
70- import java .util .Iterator ;
71- import java .util .LinkedHashMap ;
72- import java .util .List ;
73- import java .util .Locale ;
74- import java .util .Map ;
75- import java .util .Set ;
76- import java .util .TreeMap ;
70+ import java .util .*;
7771import java .util .regex .Matcher ;
7872import java .util .regex .Pattern ;
73+ import java .util .stream .Collectors ;
7974
8075/**
8176 * Writes MDL molfiles, which contains a single molecule (see {@cdk.cite DAL92}).
@@ -414,6 +409,7 @@ public void writeMolecule(IAtomContainer container) throws Exception {
414409 writer .write (line .toString ());
415410 writer .write ('\n' );
416411
412+ Set <IAtom > atomLists = new LinkedHashSet <>();
417413 // write Atom block
418414 for (int f = 0 ; f < container .getAtomCount (); f ++) {
419415 IAtom atom = container .getAtom (f );
@@ -490,6 +486,14 @@ public void writeMolecule(IAtomContainer container) throws Exception {
490486 }
491487 }
492488
489+ }else if (container .getAtom (f ) instanceof IQueryAtom ){
490+ QueryAtom queryAtom = (QueryAtom ) AtomRef .deref (container .getAtom (f ));
491+ Expr expr = queryAtom .getExpression ();
492+ String symbol = getSymbolForAtomExpression (expr );
493+ line .append (formatMDLString (symbol , 3 ));
494+ if ("L" .equals (symbol )){
495+ atomLists .add (container .getAtom (f ));
496+ }
493497 } else {
494498 line .append (formatMDLString (container .getAtom (f ).getSymbol (), 3 ));
495499 }
@@ -761,6 +765,28 @@ else if (e.equals(new Expr(Expr.Type.ALIPHATIC_ORDER, 2).or(new Expr(Expr.Type.I
761765
762766 }
763767 }
768+ //write atom lists
769+ for (IAtom a : atomLists ){
770+ QueryAtom qa = (QueryAtom ) AtomRef .deref (a );
771+ //atom lists are limited to just a list of ELEMENTS OR'ed together
772+ //with the whole expression possibly negated
773+
774+ Expr expression = qa .getExpression ();
775+ List <String > elements =getAtomList (expression );
776+ writer .write ("M ALS " );
777+ writer .write (formatMDLInt (a .getIndex ()+1 , 3 ));
778+ writer .write (formatMDLInt (elements .size (), 3 ));
779+ //root expression type is either OR or NOT
780+ if (expression .type () == Expr .Type .NOT ){
781+ writer .write (" T " );
782+ }else {
783+ writer .write (" F " );
784+ }
785+ for (String symbol : elements ){
786+ writer .write (formatMDLString (symbol , 4 ));
787+ }
788+ writer .write ('\n' );
789+ }
764790
765791 writeSgroups (container , writer , atomindex );
766792
@@ -769,7 +795,34 @@ else if (e.equals(new Expr(Expr.Type.ALIPHATIC_ORDER, 2).or(new Expr(Expr.Type.I
769795 writer .write ('\n' );
770796 writer .flush ();
771797 }
798+ private static String getSymbolForAtomExpression (Expr exp ){
799+ List <Expr > elist = new ArrayList <>();
800+ getLeafNodes (exp , elist );
801+ if (!elist .isEmpty () && elist .stream ()
802+ .allMatch (ex ->ex .type ().equals (Expr .Type .ELEMENT ))){
803+ return "L" ;
804+ }else {
805+ return "A" ;
806+ }
807+ }
808+ private static List <String > getAtomList (Expr exp ){
809+ List <Expr > elist = new ArrayList <>();
810+ getLeafNodes (exp , elist );
811+ return elist .stream ().map (expr ->Elements .ofNumber (expr .value ()).symbol ())
812+ .collect (Collectors .toList ());
813+
814+ }
772815
816+ private static void getLeafNodes (Expr exr , List <Expr > elist ){
817+ if (exr .type ().equals (Expr .Type .OR ) || exr .type ().equals (Expr .Type .AND )){
818+ getLeafNodes (exr .left (), elist );
819+ getLeafNodes (exr .right (), elist );
820+ }else if (exr .type ().equals (Expr .Type .NOT )){
821+ getLeafNodes (exr .left (), elist );
822+ }else {
823+ elist .add (exr );
824+ }
825+ }
773826 // 0 = uncharged or value other than these, 1 = +3, 2 = +2, 3 = +1,
774827 // 4 = doublet radical, 5 = -1, 6 = -2, 7 = -3
775828 private int determineCharge (IAtomContainer mol , IAtom atom ) {
0 commit comments