/* $Revision$ $Author$ $Date$ * * Copyright (C) 2004-2007 The Chemistry Development Kit (CDK) project * * This library 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 library 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, 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * (or see http://www.gnu.org/copyleft/lesser.html) */ package org.openscience.cdk.smiles.smarts.parser.visitor; import java.util.ArrayList; import java.util.List; import org.openscience.cdk.CDKConstants; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IAtomContainer; import org.openscience.cdk.interfaces.IBond; import org.openscience.cdk.isomorphism.matchers.IQueryAtom; import org.openscience.cdk.isomorphism.matchers.IQueryAtomContainer; import org.openscience.cdk.isomorphism.matchers.IQueryBond; import org.openscience.cdk.isomorphism.matchers.QueryAtomContainer; import org.openscience.cdk.isomorphism.matchers.smarts.AliphaticAtom; import org.openscience.cdk.isomorphism.matchers.smarts.AliphaticSymbolAtom; import org.openscience.cdk.isomorphism.matchers.smarts.AnyAtom; import org.openscience.cdk.isomorphism.matchers.smarts.AnyOrderQueryBond; import org.openscience.cdk.isomorphism.matchers.smarts.AromaticAtom; import org.openscience.cdk.isomorphism.matchers.smarts.AromaticOrSingleQueryBond; import org.openscience.cdk.isomorphism.matchers.smarts.AromaticQueryBond; import org.openscience.cdk.isomorphism.matchers.smarts.AromaticSymbolAtom; import org.openscience.cdk.isomorphism.matchers.smarts.AtomicNumberAtom; import org.openscience.cdk.isomorphism.matchers.smarts.ChiralityAtom; import org.openscience.cdk.isomorphism.matchers.smarts.ExplicitConnectionAtom; import org.openscience.cdk.isomorphism.matchers.smarts.FormalChargeAtom; import org.openscience.cdk.isomorphism.matchers.smarts.HybridizationNumberAtom; import org.openscience.cdk.isomorphism.matchers.smarts.HydrogenAtom; import org.openscience.cdk.isomorphism.matchers.smarts.ImplicitHCountAtom; import org.openscience.cdk.isomorphism.matchers.smarts.LogicalOperatorAtom; import org.openscience.cdk.isomorphism.matchers.smarts.LogicalOperatorBond; import org.openscience.cdk.isomorphism.matchers.smarts.MassAtom; import org.openscience.cdk.isomorphism.matchers.smarts.NonCHHeavyAtom; import org.openscience.cdk.isomorphism.matchers.smarts.OrderQueryBond; import org.openscience.cdk.isomorphism.matchers.smarts.PeriodicGroupNumberAtom; import org.openscience.cdk.isomorphism.matchers.smarts.RecursiveSmartsAtom; import org.openscience.cdk.isomorphism.matchers.smarts.RingBond; import org.openscience.cdk.isomorphism.matchers.smarts.RingIdentifierAtom; import org.openscience.cdk.isomorphism.matchers.smarts.RingMembershipAtom; import org.openscience.cdk.isomorphism.matchers.smarts.SMARTSAtom; import org.openscience.cdk.isomorphism.matchers.smarts.SMARTSBond; import org.openscience.cdk.isomorphism.matchers.smarts.SmallestRingAtom; import org.openscience.cdk.isomorphism.matchers.smarts.StereoBond; import org.openscience.cdk.isomorphism.matchers.smarts.TotalConnectionAtom; import org.openscience.cdk.isomorphism.matchers.smarts.TotalHCountAtom; import org.openscience.cdk.isomorphism.matchers.smarts.TotalRingConnectionAtom; import org.openscience.cdk.isomorphism.matchers.smarts.TotalValencyAtom; import org.openscience.cdk.smiles.smarts.parser.ASTAliphatic; import org.openscience.cdk.smiles.smarts.parser.ASTAnyAtom; import org.openscience.cdk.smiles.smarts.parser.ASTAromatic; import org.openscience.cdk.smiles.smarts.parser.ASTAtom; import org.openscience.cdk.smiles.smarts.parser.ASTAtomicMass; import org.openscience.cdk.smiles.smarts.parser.ASTAtomicNumber; import org.openscience.cdk.smiles.smarts.parser.ASTCharge; import org.openscience.cdk.smiles.smarts.parser.ASTChirality; import org.openscience.cdk.smiles.smarts.parser.ASTElement; import org.openscience.cdk.smiles.smarts.parser.ASTExplicitAtom; import org.openscience.cdk.smiles.smarts.parser.ASTExplicitConnectivity; import org.openscience.cdk.smiles.smarts.parser.ASTExplicitHighAndBond; import org.openscience.cdk.smiles.smarts.parser.ASTExplicitHighAndExpression; import org.openscience.cdk.smiles.smarts.parser.ASTGroup; import org.openscience.cdk.smiles.smarts.parser.ASTHybrdizationNumber; import org.openscience.cdk.smiles.smarts.parser.ASTImplicitHCount; import org.openscience.cdk.smiles.smarts.parser.ASTImplicitHighAndBond; import org.openscience.cdk.smiles.smarts.parser.ASTImplicitHighAndExpression; import org.openscience.cdk.smiles.smarts.parser.ASTLowAndBond; import org.openscience.cdk.smiles.smarts.parser.ASTLowAndExpression; import org.openscience.cdk.smiles.smarts.parser.ASTNonCHHeavyAtom; import org.openscience.cdk.smiles.smarts.parser.ASTNotBond; import org.openscience.cdk.smiles.smarts.parser.ASTNotExpression; import org.openscience.cdk.smiles.smarts.parser.ASTOrBond; import org.openscience.cdk.smiles.smarts.parser.ASTOrExpression; import org.openscience.cdk.smiles.smarts.parser.ASTPeriodicGroupNumber; import org.openscience.cdk.smiles.smarts.parser.ASTReaction; import org.openscience.cdk.smiles.smarts.parser.ASTRecursiveSmartsExpression; import org.openscience.cdk.smiles.smarts.parser.ASTRingConnectivity; import org.openscience.cdk.smiles.smarts.parser.ASTRingIdentifier; import org.openscience.cdk.smiles.smarts.parser.ASTRingMembership; import org.openscience.cdk.smiles.smarts.parser.ASTSimpleBond; import org.openscience.cdk.smiles.smarts.parser.ASTSmallestRingSize; import org.openscience.cdk.smiles.smarts.parser.ASTSmarts; import org.openscience.cdk.smiles.smarts.parser.ASTStart; import org.openscience.cdk.smiles.smarts.parser.ASTTotalConnectivity; import org.openscience.cdk.smiles.smarts.parser.ASTTotalHCount; import org.openscience.cdk.smiles.smarts.parser.ASTValence; import org.openscience.cdk.smiles.smarts.parser.Node; import org.openscience.cdk.smiles.smarts.parser.SMARTSParserConstants; import org.openscience.cdk.smiles.smarts.parser.SMARTSParserVisitor; import org.openscience.cdk.smiles.smarts.parser.SimpleNode; import org.openscience.cdk.tools.ILoggingTool; import org.openscience.cdk.tools.LoggingToolFactory; /** * An AST tree visitor. It builds an instance of <code>QueryAtomContainer</code> * from the AST tree. * * To use this visitor: * <pre> * SMARTSParser parser = new SMARTSParser(new java.io.StringReader("C*C")); * ASTStart ast = parser.start(); * SmartsQueryVisitor visitor = new SmartsQueryVisitor(); * QueryAtomContainer query = visitor.visit(ast, null); * </pre> * * @author Dazhi Jiao * @cdk.created 2007-04-24 * @cdk.module smarts * @cdk.githash * @cdk.keyword SMARTS AST */ public class SmartsQueryVisitor implements SMARTSParserVisitor { // current atoms with a ring identifier private RingIdentifierAtom[] ringAtoms; // current atoms in recursive smarts with a ring identifier private RingIdentifierAtom[] recursiveRingAtoms; // query private IQueryAtomContainer query; // Whether is parsing a recursive smarts private boolean isParsingRS; // Recursive smarts query private IQueryAtomContainer rsQuery; public Object visit(ASTRingIdentifier node, Object data) { IQueryAtom atom = (IQueryAtom)data; RingIdentifierAtom ringIdAtom = new RingIdentifierAtom(); ringIdAtom.setAtom(atom); IQueryBond bond; if (node.jjtGetNumChildren() == 0) { // implicit bond bond = null; } else { bond = (IQueryBond)node.jjtGetChild(0).jjtAccept(this, data); } ringIdAtom.setRingBond(bond); return ringIdAtom; } public Object visit(ASTAtom node, Object data) { IQueryAtom atom = (IQueryAtom)node.jjtGetChild(0).jjtAccept(this, data); for (int i = 1; i < node.jjtGetNumChildren(); i++) { // if there are ring identifiers ASTRingIdentifier ringIdentifier = (ASTRingIdentifier)node.jjtGetChild(i); RingIdentifierAtom ringIdAtom = (RingIdentifierAtom)ringIdentifier.jjtAccept(this, atom); // if there is already a RingIdentifierAtom, create a bond between // them and add the bond to the query int ringId = ringIdentifier.getRingId(); if (isParsingRS) { if (recursiveRingAtoms[ringId] == null) { recursiveRingAtoms[ringId] = ringIdAtom; } else { IQueryBond ringBond; // first check if the two bonds ma if (recursiveRingAtoms[ringId].getRingBond() == null) { if (ringIdAtom.getRingBond() == null) { if (atom instanceof AromaticSymbolAtom && recursiveRingAtoms[ringId].getAtom() instanceof AromaticSymbolAtom) { ringBond = new AromaticQueryBond(); } else { ringBond = new RingBond(); } } else { ringBond = ringIdAtom.getRingBond(); } } else { // Here I assume the bond are always same. This should be checked by the parser already ringBond = recursiveRingAtoms[ringId].getRingBond(); } ((IBond)ringBond).setAtoms(new IAtom[] { recursiveRingAtoms[ringId].getAtom(), atom }); rsQuery.addBond((IBond)ringBond); } // update the recursiveRingAtom reference recursiveRingAtoms[ringId] = ringIdAtom; } else { if (ringAtoms[ringId] == null) { ringAtoms[ringId] = ringIdAtom; } else { IQueryBond ringBond; // first check if the two bonds ma if (ringAtoms[ringId].getRingBond() == null) { if (ringIdAtom.getRingBond() == null) { if (atom instanceof AromaticSymbolAtom && ringAtoms[ringId].getAtom() instanceof AromaticSymbolAtom) { ringBond = new AromaticQueryBond(); } else { ringBond = new RingBond(); } } else { ringBond = ringIdAtom.getRingBond(); } } else { // Here I assume the bond are always same. This should be checked by the parser already ringBond = ringAtoms[ringId].getRingBond(); } ((IBond)ringBond).setAtoms(new IAtom[] { ringAtoms[ringId].getAtom(), atom }); query.addBond((IBond)ringBond); } // update the ringAtom reference ringAtoms[ringId] = ringIdAtom; } } return atom; } private final static ILoggingTool logger = LoggingToolFactory.createLoggingTool( SmartsQueryVisitor.class); /** * Creates a new instance */ public SmartsQueryVisitor() { super(); } public Object visit(SimpleNode node, Object data) { return null; } public Object visit(ASTStart node, Object data) { return node.jjtGetChild(0).jjtAccept(this, data); } // TODO: No QueryReaction API public Object visit(ASTReaction node, Object data) { return node.jjtGetChild(0).jjtAccept(this, data); } // TODO: No SmartsGroup API public Object visit(ASTGroup node, Object data) { List<IAtomContainer> atomContainers = new ArrayList<IAtomContainer>(); for (int i = 0; i < node.jjtGetNumChildren(); i++) { ringAtoms = new RingIdentifierAtom[10]; query = new QueryAtomContainer(); node.jjtGetChild(i).jjtAccept(this, null); atomContainers.add(query); } logger.info("Only return the first smarts. Group not supported."); return atomContainers.get(0); } public Object visit(ASTSmarts node, Object data) { SMARTSAtom atom = null; SMARTSBond bond = null; ASTAtom first = (ASTAtom)node.jjtGetChild(0); atom = (SMARTSAtom)first.jjtAccept(this, null); if (data != null) { // this is a sub smarts bond = (SMARTSBond)((Object[])data)[1]; if (bond == null) { // since no bond was specified it could be aromatic or single bond = new AromaticOrSingleQueryBond(); bond.setAtoms(new IAtom[] {atom, (SMARTSAtom)((Object[])data)[0]}); } else { bond.setAtoms(new IAtom[] {(SMARTSAtom)((Object[])data)[0], atom}); } if (isParsingRS) rsQuery.addBond(bond); else query.addBond(bond); bond = null; } if (isParsingRS) rsQuery.addAtom(atom); else query.addAtom(atom); for (int i = 1; i < node.jjtGetNumChildren(); i++) { Node child = node.jjtGetChild(i); if (child instanceof ASTLowAndBond) { bond = (SMARTSBond) child.jjtAccept(this, data); } else if (child instanceof ASTAtom) { SMARTSAtom newAtom = (SMARTSAtom)child.jjtAccept(this, null); if (bond == null) { // since no bond was specified it could be aromatic or single bond = new AromaticOrSingleQueryBond(); } bond.setAtoms(new IAtom[] {atom, newAtom}); if (isParsingRS) { rsQuery.addBond(bond); rsQuery.addAtom(newAtom); } else { query.addBond(bond); query.addAtom(newAtom); } atom = newAtom; bond = null; } else if (child instanceof ASTSmarts) { // another smarts child.jjtAccept(this, new Object[] {atom, bond}); bond = null; } } return isParsingRS ? rsQuery: query; } public Object visit(ASTNotBond node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.getType() == SMARTSParserConstants.DEFAULT) { return left; } LogicalOperatorBond bond = new LogicalOperatorBond(); bond.setOperator("not"); bond.setLeft((IQueryBond) left); return bond; } public Object visit(ASTImplicitHighAndBond node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.jjtGetNumChildren() == 1) { return left; } LogicalOperatorBond bond = new LogicalOperatorBond(); bond.setOperator("and"); bond.setLeft((IQueryBond) left); IQueryBond right = (IQueryBond) node.jjtGetChild(1).jjtAccept(this, data); bond.setRight(right); return bond; } public Object visit(ASTLowAndBond node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.jjtGetNumChildren() == 1) { return left; } LogicalOperatorBond bond = new LogicalOperatorBond(); bond.setOperator("and"); bond.setLeft((IQueryBond) left); IQueryBond right = (IQueryBond) node.jjtGetChild(1).jjtAccept(this, data); bond.setRight(right); return bond; } public Object visit(ASTOrBond node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.jjtGetNumChildren() == 1) { return left; } LogicalOperatorBond bond = new LogicalOperatorBond(); bond.setOperator("or"); bond.setLeft((IQueryBond) left); IQueryBond right = (IQueryBond) node.jjtGetChild(1).jjtAccept(this, data); bond.setRight(right); return bond; } public Object visit(ASTExplicitHighAndBond node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.jjtGetNumChildren() == 1) { return left; } LogicalOperatorBond bond = new LogicalOperatorBond(); bond.setOperator("and"); bond.setLeft((IQueryBond) left); IQueryBond right = (IQueryBond) node.jjtGetChild(1).jjtAccept(this, data); bond.setRight(right); return bond; } public Object visit(ASTSimpleBond node, Object data) { SMARTSBond bond = null; switch (node.getBondType()) { case SMARTSParserConstants.S_BOND: bond = new OrderQueryBond(IBond.Order.SINGLE); break; case SMARTSParserConstants.D_BOND: bond = new OrderQueryBond(IBond.Order.DOUBLE); break; case SMARTSParserConstants.T_BOND: bond = new OrderQueryBond(IBond.Order.TRIPLE); break; case SMARTSParserConstants.ANY_BOND: bond = new AnyOrderQueryBond(); break; case SMARTSParserConstants.AR_BOND: bond = new AromaticQueryBond(); break; case SMARTSParserConstants.R_BOND: bond = new RingBond(); break; case SMARTSParserConstants.UP_S_BOND: bond = new StereoBond(); bond.setOrder(IBond.Order.SINGLE); bond.setStereo(IBond.Stereo.UP); break; case SMARTSParserConstants.DN_S_BOND: bond = new StereoBond(); bond.setOrder(IBond.Order.SINGLE); bond.setStereo(IBond.Stereo.DOWN); break; case SMARTSParserConstants.UP_OR_UNSPECIFIED_S_BOND: LogicalOperatorBond logical = new LogicalOperatorBond(); logical.setOperator("or"); StereoBond bond1 = new StereoBond(); bond1.setOrder(IBond.Order.SINGLE); bond1.setStereo(IBond.Stereo.UP); logical.setLeft(bond1); StereoBond bond2 = new StereoBond(); bond2.setOrder(IBond.Order.SINGLE); bond2.setStereo((IBond.Stereo)CDKConstants.UNSET); logical.setRight(bond2); bond = logical; break; case SMARTSParserConstants.DN_OR_UNSPECIFIED_S_BOND: logical = new LogicalOperatorBond(); logical.setOperator("or"); bond1 = new StereoBond(); bond1.setOrder(IBond.Order.SINGLE); bond1.setStereo(IBond.Stereo.DOWN); logical.setLeft(bond1); bond2 = new StereoBond(); bond2.setOrder(IBond.Order.SINGLE); bond2.setStereo((IBond.Stereo)CDKConstants.UNSET); logical.setRight(bond2); bond = logical; break; default: logger.error("Un parsed bond: " + node.toString()); break; } return bond; } public Object visit(ASTRecursiveSmartsExpression node, Object data) { rsQuery = new QueryAtomContainer(); recursiveRingAtoms = new RingIdentifierAtom[10]; isParsingRS = true; node.jjtGetChild(0).jjtAccept(this, null); isParsingRS = false; return new RecursiveSmartsAtom(rsQuery); } public ASTStart getRoot(Node node) { if (node instanceof ASTStart) { return (ASTStart) node; } return getRoot(node.jjtGetParent()); } public Object visit(ASTElement node, Object data) { String symbol = node.getSymbol(); SMARTSAtom atom; if ("o".equals(symbol) || "n".equals(symbol) || "c".equals(symbol) || "s".equals(symbol) || "p".equals(symbol) || "as".equals(symbol) || "se".equals(symbol)) { String atomSymbol = symbol.substring(0,1).toUpperCase() + symbol.substring(1); atom = new AromaticSymbolAtom(atomSymbol); } else { atom = new AliphaticSymbolAtom(symbol); } return atom; } public Object visit(ASTTotalHCount node, Object data) { return new TotalHCountAtom(node.getCount()); } public Object visit(ASTImplicitHCount node, Object data) { return new ImplicitHCountAtom(node.getCount()); } public Object visit(ASTExplicitConnectivity node, Object data) { return new ExplicitConnectionAtom(node.getNumOfConnection()); } public Object visit(ASTAtomicNumber node, Object data) { return new AtomicNumberAtom(node.getNumber()); } public Object visit(ASTHybrdizationNumber node, Object data) { return new HybridizationNumberAtom(node.getHybridizationNumber()); } public Object visit(ASTCharge node, Object data) { if (node.isPositive()) { return new FormalChargeAtom(node.getCharge()); } else { return new FormalChargeAtom(0 - node.getCharge()); } } public Object visit(ASTRingConnectivity node, Object data) { return new TotalRingConnectionAtom(node.getNumOfConnection()); } public Object visit(ASTPeriodicGroupNumber node, Object data) { return new PeriodicGroupNumberAtom(node.getGroupNumber()); } public Object visit(ASTTotalConnectivity node, Object data) { return new TotalConnectionAtom(node.getNumOfConnection()); } public Object visit(ASTValence node, Object data) { return new TotalValencyAtom(node.getOrder()); } public Object visit(ASTRingMembership node, Object data) { return new RingMembershipAtom(node.getNumOfMembership()); } public Object visit(ASTSmallestRingSize node, Object data) { return new SmallestRingAtom(node.getSize()); } public Object visit(ASTAliphatic node, Object data) { return new AliphaticAtom(); } public Object visit(ASTNonCHHeavyAtom node, Object data) { return new NonCHHeavyAtom(); } public Object visit(ASTAromatic node, Object data) { return new AromaticAtom(); } public Object visit(ASTAnyAtom node, Object data) { return new AnyAtom(); } public Object visit(ASTAtomicMass node, Object data) { return new MassAtom(node.getMass()); } public Object visit(ASTChirality node, Object data) { ChiralityAtom atom = new ChiralityAtom(); atom.setDegree(node.getDegree()); atom.setClockwise(node.isClockwise()); atom.setUnspecified(node.isUnspecified()); return atom; } public Object visit(ASTLowAndExpression node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.jjtGetNumChildren() == 1) { return left; } LogicalOperatorAtom atom = new LogicalOperatorAtom(); atom.setOperator("and"); atom.setLeft((IQueryAtom) left); IQueryAtom right = (IQueryAtom) node.jjtGetChild(1).jjtAccept(this, data); atom.setRight(right); return atom; } public Object visit(ASTOrExpression node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.jjtGetNumChildren() == 1) { return left; } LogicalOperatorAtom atom = new LogicalOperatorAtom(); atom.setOperator("or"); atom.setLeft((IQueryAtom) left); IQueryAtom right = (IQueryAtom) node.jjtGetChild(1).jjtAccept(this, data); atom.setRight(right); return atom; } public Object visit(ASTNotExpression node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.getType() == SMARTSParserConstants.DEFAULT) { return left; } LogicalOperatorAtom atom = new LogicalOperatorAtom(); atom.setOperator("not"); atom.setLeft((IQueryAtom) left); return atom; } public Object visit(ASTExplicitHighAndExpression node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.jjtGetNumChildren() == 1) { return left; } LogicalOperatorAtom atom = new LogicalOperatorAtom(); atom.setOperator("and"); atom.setLeft((IQueryAtom) left); IQueryAtom right = (IQueryAtom) node.jjtGetChild(1).jjtAccept(this, data); atom.setRight(right); return atom; } public Object visit(ASTImplicitHighAndExpression node, Object data) { Object left = node.jjtGetChild(0).jjtAccept(this, data); if (node.jjtGetNumChildren() == 1) { return left; } LogicalOperatorAtom atom = new LogicalOperatorAtom(); atom.setOperator("and"); atom.setLeft((IQueryAtom) left); IQueryAtom right = (IQueryAtom) node.jjtGetChild(1).jjtAccept(this, data); atom.setRight(right); return atom; } public Object visit(ASTExplicitAtom node, Object data) { IQueryAtom atom = null; String symbol = node.getSymbol(); if ("*".equals(symbol)) { atom = new AnyAtom(); } else if ("A".equals(symbol)) { atom = new AliphaticAtom(); } else if ("a".equals(symbol)) { atom = new AromaticAtom(); } else if ("o".equals(symbol) || "n".equals(symbol) || "c".equals(symbol) || "s".equals(symbol) || "p".equals(symbol) || "as".equals(symbol) || "se".equals(symbol)) { String atomSymbol = symbol.substring(0,1).toUpperCase() + symbol.substring(1); atom = new AromaticSymbolAtom(atomSymbol); } else if ("H".equals(symbol)) { atom = new HydrogenAtom(); atom.setSymbol(symbol.toUpperCase()); atom.setMassNumber(1); } else if ("D".equals(symbol)) { atom = new HydrogenAtom(); atom.setSymbol(symbol.toUpperCase()); atom.setMassNumber(2); } else if ("T".equals(symbol)) { atom = new HydrogenAtom(); atom.setSymbol(symbol.toUpperCase()); atom.setMassNumber(3); } else { atom = new AliphaticSymbolAtom(symbol); } return atom; } }