/* $Revision$ $Author$ $Date$ * * Copyright (C) 2002-2007 Egon Willighagen <egonw@users.sf.net> * * Contact: cdk-devel@lists.sourceforge.net * * This program 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. * All we ask is that proper credit is given for our work, which includes * - but is not limited to - adding the above copyright notice to the beginning * of your source code files, and to any copyright notice that you may distribute * with programs based on this work. * * This program 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * */ package org.openscience.cdk.io; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.util.StringTokenizer; import javax.vecmath.Point3d; import javax.vecmath.Vector3d; import org.openscience.cdk.annotations.TestClass; import org.openscience.cdk.annotations.TestMethod; import org.openscience.cdk.exception.CDKException; import org.openscience.cdk.geometry.CrystalGeometryTools; import org.openscience.cdk.interfaces.IAtom; import org.openscience.cdk.interfaces.IChemFile; import org.openscience.cdk.interfaces.IChemModel; import org.openscience.cdk.interfaces.IChemObject; import org.openscience.cdk.interfaces.IChemSequence; import org.openscience.cdk.interfaces.ICrystal; import org.openscience.cdk.io.formats.IResourceFormat; import org.openscience.cdk.io.formats.ShelXFormat; import org.openscience.cdk.math.FortranFormat; import org.openscience.cdk.tools.ILoggingTool; import org.openscience.cdk.tools.LoggingToolFactory; /** * A reader for ShelX output (RES) files. It does not read all information. * The list of fields that is read: REM, END, CELL, SPGR. * In additions atoms are read. * * <p>A reader for ShelX files. It currently supports ShelXL. * * <p>The ShelXL format is described on the net: * <a href="http://www.msg.ucsf.edu/local/programs/shelxl/ch_07.html" * http://www.msg.ucsf.edu/local/programs/shelxl/ch_07.html</a>. * * @cdk.module io * @cdk.githash * * @cdk.keyword file format, ShelXL * @author E.L. Willighagen */ @TestClass("org.openscience.cdk.io.ShelXReaderTest") public class ShelXReader extends DefaultChemObjectReader { private BufferedReader input; private static ILoggingTool logger = LoggingToolFactory.createLoggingTool(ShelXReader.class); /** * Create an ShelX file reader. * * @param input source of ShelX data */ public ShelXReader(Reader input) { this.input = new BufferedReader(input); } public ShelXReader(InputStream input) { this(new InputStreamReader(input)); } public ShelXReader() { this(new StringReader("")); } @TestMethod("testGetFormat") public IResourceFormat getFormat() { return ShelXFormat.getInstance(); } @TestMethod("testSetReader_Reader") public void setReader(Reader input) throws CDKException { if (input instanceof BufferedReader) { this.input = (BufferedReader)input; } else { this.input = new BufferedReader(input); } } @TestMethod("testSetReader_InputStream") public void setReader(InputStream input) throws CDKException { setReader(new InputStreamReader(input)); } @TestMethod("testAccepts") public boolean accepts(Class classObject) { Class[] interfaces = classObject.getInterfaces(); for (int i=0; i<interfaces.length; i++) { if (ICrystal.class.equals(interfaces[i])) return true; if (IChemFile.class.equals(interfaces[i])) return true; } Class superClass = classObject.getSuperclass(); if (superClass != null) return this.accepts(superClass); return false; } /** * Read a ChemFile from input * * @return the content in a ChemFile object */ public IChemObject read(IChemObject object) throws CDKException { if (object instanceof IChemFile) { try { return readChemFile((IChemFile)object); } catch (IOException e) { logger.error("Input/Output error while reading from input: " + e.getMessage()); throw new CDKException(e.getMessage(), e); } } else if (object instanceof ICrystal) { try { return readCrystal((ICrystal)object); } catch (IOException e) { logger.error("Input/Output error while reading from input: " + e.getMessage()); throw new CDKException(e.getMessage(), e); } } else { throw new CDKException("Only supported is reading of ChemFile."); } } /** * Read the ShelX from input. Each ShelX document is expected to contain * one crystal structure. * * @return a ChemFile with the coordinates, charges, vectors, etc. */ private IChemFile readChemFile(IChemFile file) throws IOException { IChemSequence seq = file.getBuilder().newChemSequence(); IChemModel model = file.getBuilder().newChemModel(); ICrystal crystal = readCrystal(file.getBuilder().newCrystal()); model.setCrystal(crystal); seq.addChemModel(model); file.addChemSequence(seq); return file; } private ICrystal readCrystal(ICrystal crystal) throws IOException { String line = input.readLine(); boolean end_found = false; while (input.ready() && line != null && !end_found) { /* is line continued? */ if (line.length() > 0 && line.substring(line.length()-1).equals("=")) { /* yes, line is continued */ line = line + input.readLine(); } /* determine ShelX command */ String command; try { command = new String(line.substring(0, 4)); } catch (StringIndexOutOfBoundsException sioobe) { // disregard this line break; } logger.debug("command: " + command); if (command.substring(0,3).equalsIgnoreCase("REM")) { /* line is comment, disregard */ /* 7.1 Crystal data and general instructions */ } else if (command.substring(0,3).equalsIgnoreCase("END")) { end_found = true; } else if (command.equalsIgnoreCase("TITL")) { } else if (command.equalsIgnoreCase("CELL")) { /* example: * CELL 1.54184 23.56421 7.13203 18.68928 90.0000 109.3799 90.0000 * CELL 1.54184 7.11174 21.71704 30.95857 90.000 90.000 90.000 */ StringTokenizer st = new StringTokenizer(line); st.nextToken(); // String command_again st.nextToken(); // String wavelength String sa = st.nextToken(); String sb = st.nextToken(); String sc = st.nextToken(); String salpha = st.nextToken(); String sbeta = st.nextToken(); String sgamma = st.nextToken(); logger.debug("a: " + sa); logger.debug("b: " + sb); logger.debug("c: " + sc); logger.debug("alpha: " + salpha); logger.debug("beta : " + sbeta); logger.debug("gamma: " + sgamma); double a = FortranFormat.atof(sa); double b = FortranFormat.atof(sb); double c = FortranFormat.atof(sc); double alpha = FortranFormat.atof(salpha); double beta = FortranFormat.atof(sbeta); double gamma = FortranFormat.atof(sgamma); Vector3d[] axes = CrystalGeometryTools.notionalToCartesian(a,b,c, alpha, beta, gamma); crystal.setA(axes[0]); crystal.setB(axes[1]); crystal.setC(axes[2]); } else if (command.equalsIgnoreCase("ZERR")) { } else if (command.equalsIgnoreCase("LATT")) { } else if (command.equalsIgnoreCase("SYMM")) { } else if (command.equalsIgnoreCase("SFAC")) { } else if (command.equalsIgnoreCase("DISP")) { } else if (command.equalsIgnoreCase("UNIT")) { } else if (command.equalsIgnoreCase("LAUE")) { } else if (command.equalsIgnoreCase("REM ")) { } else if (command.equalsIgnoreCase("MORE")) { } else if (command.equalsIgnoreCase("TIME")) { /* 7.2 Reflection data input */ } else if (command.equalsIgnoreCase("HKLF")) { } else if (command.equalsIgnoreCase("OMIT")) { } else if (command.equalsIgnoreCase("SHEL")) { } else if (command.equalsIgnoreCase("BASF")) { } else if (command.equalsIgnoreCase("TWIN")) { } else if (command.equalsIgnoreCase("EXTI")) { } else if (command.equalsIgnoreCase("SWAT")) { } else if (command.equalsIgnoreCase("HOPE")) { } else if (command.equalsIgnoreCase("MERG")) { /* 7.3 Atom list and least-squares constraints */ } else if (command.equalsIgnoreCase("SPEC")) { } else if (command.equalsIgnoreCase("RESI")) { } else if (command.equalsIgnoreCase("MOVE")) { } else if (command.equalsIgnoreCase("ANIS")) { } else if (command.equalsIgnoreCase("AFIX")) { } else if (command.equalsIgnoreCase("HFIX")) { } else if (command.equalsIgnoreCase("FRAG")) { } else if (command.equalsIgnoreCase("FEND")) { } else if (command.equalsIgnoreCase("EXYZ")) { } else if (command.equalsIgnoreCase("EXTI")) { } else if (command.equalsIgnoreCase("EADP")) { } else if (command.equalsIgnoreCase("EQIV")) { /* 7.4 The connectivity list */ } else if (command.equalsIgnoreCase("CONN")) { } else if (command.equalsIgnoreCase("PART")) { } else if (command.equalsIgnoreCase("BIND")) { } else if (command.equalsIgnoreCase("FREE")) { /* 7.5 Least-squares restraints */ } else if (command.equalsIgnoreCase("DFIX")) { } else if (command.equalsIgnoreCase("DANG")) { } else if (command.equalsIgnoreCase("BUMP")) { } else if (command.equalsIgnoreCase("SAME")) { } else if (command.equalsIgnoreCase("SADI")) { } else if (command.equalsIgnoreCase("CHIV")) { } else if (command.equalsIgnoreCase("FLAT")) { } else if (command.equalsIgnoreCase("DELU")) { } else if (command.equalsIgnoreCase("SIMU")) { } else if (command.equalsIgnoreCase("DEFS")) { } else if (command.equalsIgnoreCase("ISOR")) { } else if (command.equalsIgnoreCase("NCSY")) { } else if (command.equalsIgnoreCase("SUMP")) { /* 7.6 Least-squares organization */ } else if (command.equalsIgnoreCase("L.S.")) { } else if (command.equalsIgnoreCase("CGLS")) { } else if (command.equalsIgnoreCase("BLOC")) { } else if (command.equalsIgnoreCase("DAMP")) { } else if (command.equalsIgnoreCase("STIR")) { } else if (command.equalsIgnoreCase("WGHT")) { } else if (command.equalsIgnoreCase("FVAR")) { /* 7.7 Lists and tables */ } else if (command.equalsIgnoreCase("BOND")) { } else if (command.equalsIgnoreCase("CONF")) { } else if (command.equalsIgnoreCase("MPLA")) { } else if (command.equalsIgnoreCase("RTAB")) { } else if (command.equalsIgnoreCase("HTAB")) { } else if (command.equalsIgnoreCase("LIST")) { } else if (command.equalsIgnoreCase("ACTA")) { } else if (command.equalsIgnoreCase("SIZE")) { } else if (command.equalsIgnoreCase("TEMP")) { } else if (command.equalsIgnoreCase("WPDB")) { /* 7.8 Fouriers, peak search and lineprinter plots */ } else if (command.equalsIgnoreCase("FMAP")) { } else if (command.equalsIgnoreCase("GRID")) { } else if (command.equalsIgnoreCase("PLAN")) { } else if (command.equalsIgnoreCase("MOLE")) { /* NOT DOCUMENTED BUT USED BY PLATON */ } else if (command.equalsIgnoreCase("SPGR")) { // Line added by PLATON stating the spacegroup StringTokenizer st = new StringTokenizer(line); st.nextToken(); // String command_again String spacegroup = st.nextToken(); crystal.setSpaceGroup(spacegroup); } else if (command.equalsIgnoreCase(" ")) { logger.debug("Disrgarding line assumed to be added by PLATON: " + line); /* All other is atom */ } else { //logger.debug("Assumed to contain an atom: " + line); /* this line gives an atom, because all lines not starting with a ShelX command is an atom (that sucks!) */ StringTokenizer st = new StringTokenizer(line); String atype = st.nextToken(); st.nextToken(); // String scatt_factor String sa = st.nextToken(); String sb = st.nextToken(); String sc = st.nextToken(); // skip the rest if (Character.isDigit(atype.charAt(1))) { // atom type has a one letter code atype = atype.substring(0,1); } else { StringBuffer sb2 = new StringBuffer(); sb2.append(atype.charAt(1)); atype = atype.substring(0,1) + sb2.toString().toLowerCase(); } double[] frac = new double[3]; frac[0] = FortranFormat.atof(sa); // fractional coordinates frac[1] = FortranFormat.atof(sb); frac[2] = FortranFormat.atof(sc); logger.debug("fa,fb,fc: " + frac[0] + ", " + frac[1] + ", " + frac[2]); if (atype.equalsIgnoreCase("Q")) { // ingore atoms named Q } else { logger.info("Adding atom: " + atype + ", " + frac[0] + ", " + frac[1] + ", " + frac[2]); IAtom atom = crystal.getBuilder().newAtom(atype); atom.setFractionalPoint3d(new Point3d(frac[0], frac[1], frac[2])); crystal.addAtom(atom); logger.debug("Atom added: ", atom); } } line = input.readLine(); } return crystal; } @TestMethod("testClose") public void close() throws IOException { input.close(); } }