All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nih.ncats.molwitch.cdk.CdkAtom Maven / Gradle / Ivy

/*
 * NCATS-MOLWITCH-CDK
 *
 * Copyright (c) 2019.
 *
 * This work is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any later version.
 *
 * This work is distributed in the hope that it will be useful, but without any warranty;
 * without even the implied warranty of merchantability or fitness for a particular purpose.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this library;
 *  if not, write to:
 *
 *  the Free Software Foundation, Inc.
 *  59 Temple Place, Suite 330
 *  Boston, MA 02111-1307 USA
 */

package gov.nih.ncats.molwitch.cdk;

import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;

import javax.vecmath.Point2d;
import javax.vecmath.Point3d;

import org.openscience.cdk.AtomRef;
import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.SingleElectron;
import org.openscience.cdk.config.IsotopeFactory;
import org.openscience.cdk.config.Isotopes;
import org.openscience.cdk.graph.Cycles;
import org.openscience.cdk.interfaces.*;
import org.openscience.cdk.isomorphism.matchers.IQueryAtom;
import org.openscience.cdk.isomorphism.matchers.RGroupQuery;
import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;

import gov.nih.ncats.molwitch.Atom;
import gov.nih.ncats.molwitch.AtomCoordinates;
import gov.nih.ncats.molwitch.Bond;
import gov.nih.ncats.molwitch.Chirality;
import uk.ac.ebi.beam.Element;

public class CdkAtom implements Atom{

	private static IsotopeFactory isotopeFactory;
	
	static {
		try {
		isotopeFactory = Isotopes.getInstance();
		}catch(IOException e) {
			throw new IllegalStateException("error loading isotope data", e);
		}
	}
	
	private IAtom atom;
	private CdkChemicalImpl parent;
	
	public static IAtom getIAtomFor(Atom a){
		return ((CdkAtom)a).atom;
	}
	
	public CdkAtom(IAtom atom, CdkChemicalImpl parent) {
		Objects.requireNonNull(atom);
		
		this.parent = parent;
		this.atom = atom;
		
	}
	
	

	@Override
	public boolean isValidAtomicSymbol() {
		return Element.ofSymbol(getSymbol()) !=null;
	}

	@Override
	public int getSmallestRingSize() {
		//this should set the ISINRING flags
		parent.ringsSearcherSupplier.get();
		if( !atom.isInRing()) {
			return 0;
		};
	
		
		IRingSet ringSet = Cycles.sssr(((IAtomContainer) parent.getWrappedObject())).toRingSet();
		if(!ringSet.contains(atom)) {
			return 0;
		}
		int min = Integer.MAX_VALUE;
		for(IAtomContainer r : ringSet.atomContainers()) {
			if(r.contains(atom)) {
				int len = r.getAtomCount();
				if(len < min) {
					min = len;
				}
			}
		}
		
		return min;
	}



	@Override
	public boolean isQueryAtom() {
		return AtomRef.deref(atom) instanceof IQueryAtom;
	}



	@Override
	public OptionalInt getAtomToAtomMap() {
		Object atomAtomMapping = atom.getProperty(CDKConstants.ATOM_ATOM_MAPPING);
		if(atomAtomMapping ==null) {
			return OptionalInt.empty();
		}
		int value=0;
		if (atomAtomMapping instanceof String) {
            	//this will throw a IllegalArgumentException (runtime)
            value = Integer.parseInt((String) atomAtomMapping);
		}else if(atomAtomMapping instanceof Integer) {
			value = (Integer) atomAtomMapping;
		}
		if(value ==0) {
			return OptionalInt.empty();
		}
		return OptionalInt.of(value);
	}

	@Override
	public void setAtomToAtomMap(int value) {
		if(value ==0) {
			atom.removeProperty(CDKConstants.ATOM_ATOM_MAPPING);
		}else {
			atom.setProperty(CDKConstants.ATOM_ATOM_MAPPING, value);
		}
		
	}

	@Override
	public boolean isInRing() {
		//this should set the ISINRING flags
		parent.ringsSearcherSupplier.get();
		return atom.isInRing();
	}

	@Override
	public String getSymbol() {
		return atom.getSymbol();
	}
	
	

	@Override
	public boolean isIsotope() {
		Integer mass = atom.getMassNumber();
		if(mass ==null) {
			return false;
		}
		return !(mass.equals(isotopeFactory.getMajorIsotope(atom.getSymbol()).getMassNumber()));
	}

	@Override
	public List getBonds() {
		return parent.getBondsFor(atom);
	}

	IAtom getAtom() {
		return atom;
	}

	@Override
	public int getAtomicNumber() {
		return atom.getAtomicNumber();
	}

	@Override
	public void setAtomicNumber(int atomicNumber) {
		atom.setAtomicNumber(atomicNumber);
	}

	@Override
	public boolean hasAromaticBond() {
		return atom.isAromatic();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + atom.hashCode();
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		CdkAtom other = (CdkAtom) obj;
		if (!atom.equals(other.atom))
			return false;
		return true;
	}

	@Override
	public int getCharge() {
		Integer charge = atom.getFormalCharge();
		//assume unset = 0 ?
		if(charge ==null || CDKConstants.UNSET == charge){
			return 0;
		}
		return charge.intValue();
	}

	@Override
	public int getRadical() {
		List list = parent.getContainer().getConnectedSingleElectronsList(atom);
		if(list.isEmpty()) {
			return 0;
		}
		return list.stream().map(ise -> ise.getElectronCount()).filter(Objects::nonNull).mapToInt(Integer::intValue).sum();
	}

	@Override
	public void setRadical(int radical) {

		ISingleElectron ise = new SingleElectron(atom);
		ise.setElectronCount(radical);

		IAtomContainer container = parent.getContainer();
		container.getConnectedSingleElectronsList(atom)
								.forEach(container::removeSingleElectron);

		container.addSingleElectron(ise);

	}


	@Override
	public AtomCoordinates getAtomCoordinates() {
		Point3d points = atom.getPoint3d();
		if(points !=null) {
			return AtomCoordinates.valueOf(points.x, points.y, points.z);
		}
		Point2d otherPoints = atom.getPoint2d();
		if(otherPoints !=null) {
			return AtomCoordinates.valueOf(otherPoints.x, otherPoints.y);
		}
		return null;
	}

	@Override
	public void setAtomCoordinates(AtomCoordinates atomCoordinates) {
		if(atomCoordinates ==null) {
			atom.setPoint2d(null);
			atom.setPoint3d(null);
		}else if(atomCoordinates.is3D()) {
			atom.setPoint2d(null);
			atom.setPoint3d(new Point3d(atomCoordinates.getX(), atomCoordinates.getY(), atomCoordinates.getZ().getAsDouble()));
		}else {
			atom.setPoint3d(null);
			atom.setPoint2d(new Point2d(atomCoordinates.getX(), atomCoordinates.getY()));
		
		}
		
	}

	
	

	@Override
	public Chirality getChirality() {
		parent.cahnIngoldPrelogSupplier.get();
		String value = atom.getProperty(CDKConstants.CIP_DESCRIPTOR);
		if("R".equals(value)) {
			return Chirality.R;
		}
		if("S".equals(value)) {
			return Chirality.S;
		}
		return Chirality.Non_Chiral;
	}

	@Override
	public void setChirality(Chirality chirality) {

	}

	@Override
	public int getMassNumber() {
		Integer num= atom.getMassNumber();
		if(num==null){
			return 0;
		}
		return num.intValue();
	}
	

	@Override
	public double getExactMass() {
		// TODO Auto-generated method stub
		Double d= atom.getExactMass();
		if(d ==null) {
			return 0;
		}
		return d.doubleValue();
	}

	@Override
	public void setCharge(int charge) {
		atom.setFormalCharge(charge);
		recomputeImplicitHydrogens();
	}

	private void recomputeImplicitHydrogens() {
		atom.setImplicitHydrogenCount(null);
		parent.setImplicitHydrogens(atom);
	}

	@Override
	public void setMassNumber(int mass) {
		if(mass <0){
			throw new IllegalArgumentException("mass can not be negative");
		}
		if(mass ==0){
			atom.setMassNumber(null);
		}else{
			atom.setMassNumber(mass);
		}
		recomputeImplicitHydrogens();
	}

	@Override
	public int getImplicitHCount() {
		Integer ret = atom.getImplicitHydrogenCount();
		if(ret ==null){
			//bug in hydrogen count
			if(isQueryAtom()){
				return 0;
			}
			//compute it
			return parent.setImplicitHydrogens(atom);
			
		}
		return ret;
	}
	

	@Override
	public void setImplicitHCount(Integer implicitH) {
		atom.setImplicitHydrogenCount(implicitH);
	}

	@Override
	public int getRadicalValue() {
		return parent.getContainer().getConnectedSingleElectronsCount(atom);
		//returns 1 for double? 2 for triplet?
//		System.out.println("--------");
//		for(IElectronContainer c : parent.getContainer().getConnectedElectronContainersList(atom)) {
//			System.out.println("electron container has # electrons " + c.getElectronCount());
//		}
//		
//		List singleElectrons = parent.getContainer().getConnectedSingleElectronsList(atom);
//		int total = 0;
//		System.out.println("======");
//		for(ISingleElectron se : singleElectrons) {
//			total +=se.getElectronCount();
//		}
//		System.out.println(this.toString() +  " single electrong list size = " + singleElectrons.size() + "  total count = " + total);
//		return total; 
	}

	@Override
	public OptionalInt getValence() {
		parent.perceiveAtomTypesOfNonQueryAtoms.get();
		Integer valence= atom.getValency();
		
		if(valence ==null) {
			return OptionalInt.empty();
		}
		return OptionalInt.of(valence);
	}
	

	@Override
	public boolean hasValenceError() {
		//after perceiving atom types valence should not be null unless there's an error?
		//TODO maybe not set on query atoms?
		return !getValence().isPresent();
	}

	@Override
	public boolean isRGroupAtom() {
		return getRGroupIndex().isPresent();
	}

	@Override
	public OptionalInt getRGroupIndex() {
		
		return getPseudoAtomField(iPseudoAtom -> {
			String label = iPseudoAtom.getLabel();
			 if(label !=null && RGroupQuery.isValidRgroupQueryLabel(label)) {
				 return OptionalInt.of(Integer.parseInt(label.substring(1)));
			 }
			 return OptionalInt.empty();
		},
				OptionalInt::empty);

	}


	@Override
	public void setRGroup(Integer rGroup) {
		if(rGroup ==null || rGroup < 1) {
			if(isRGroupAtom()) {
				setAlias(null);
			}
		}else {
			setAlias("R"+rGroup);
		}
	}

	private  T getPseudoAtomField(Function function, Supplier emptySupplier) {
		if(atom instanceof IPseudoAtom) {
			IPseudoAtom iPseudoAtom = (IPseudoAtom)atom;
			return function.apply(iPseudoAtom);
		}
		return emptySupplier.get();
	}

	@Override
	public Optional getAlias() {
		return getPseudoAtomField( a-> Optional.ofNullable(a.getLabel()), Optional::empty);
	}

	@Override
	public void setAlias(String alias) {
		changeToPseudoAtomIfNeeded((pseudoAtom, newObj) -> {
			pseudoAtom.setLabel(alias);
			if(newObj) {
				pseudoAtom.setSymbol(alias);
			}
			
		});
		
	}
	
	

	//borrowed from CDK MDLV20000Reader with minor adjustments
	//to make it take consumers for various operations
	
	private void changeToPseudoAtomIfNeeded(BiConsumer consumer) {
		IAtomContainer container = parent.getContainer();
	 final IPseudoAtom pseudoAtom = atom instanceof IPseudoAtom ? (IPseudoAtom) atom : container.getBuilder()
             .newInstance(IPseudoAtom.class);
     if (atom.equals(pseudoAtom)) {
    	 consumer.accept(pseudoAtom, false);
     } else {
    	 //some consumers might change the symbol but by default reuse atom symbol
         pseudoAtom.setSymbol(atom.getSymbol());
         pseudoAtom.setAtomicNumber(0);
         pseudoAtom.setPoint2d(atom.getPoint2d());
         pseudoAtom.setPoint3d(atom.getPoint3d());
         pseudoAtom.setMassNumber(atom.getMassNumber());
         pseudoAtom.setFormalCharge(atom.getFormalCharge());
         pseudoAtom.setValency(atom.getValency());
         
         consumer.accept(pseudoAtom, true);
         //katzelda -chemkit note : need to update atom and bond caches
 		//Looking into the source code for this method
         //it doesn't make  new IBond objects just
         //updates the fields for  IBond objects
         //so we should  be OK and don't have to update our caches...
         
 		 // XXX: would be faster to track all replacements and do it all in one
         AtomContainerManipulator.replaceAtomByAtom(container, atom, pseudoAtom);
        
         this.atom = pseudoAtom;
     }
	}

	@Override
	public String toString() {
		return "CdkAtom [atom=" + atom + "]";
	}



	@Override
	public int getAtomIndexInParent() {
		return atom.getIndex();
	}

	
	

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy