package name.mjw.jamber.IO.AMBER;
import name.mjw.jamber.IO.Mol2;
import org.apache.log4j.Logger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
* AMBER lib/OFF file format.
* <p>
* This is essentially a collection of {@link Residue}s, manifest from the AMBER
* lib/OFF file <a href="http://ambermd.org/doc/OFF_file_format.txt">format</a>.
* <p>
* Residue information may also be added from Mol2 files, since AnteChamber
* prefers to encapsulate such information in this format rather than the lib/OFF
* format.
*
* @author mw529
*
*/
public class Lib {
private final int BUFFER_SIZE = 5000;
private final Logger LOG = Logger.getLogger(Lib.class);
private final ArrayList<Residue> residues = new ArrayList<>();
public Lib(Mol2 mol) {
addFromMol2(mol);
}
public Lib(InputStream... ises) throws IOException {
try {
read(ises);
} catch (ParseException e) {
e.printStackTrace();
}
}
/**
* Sub parsing method; reads a specific !entry.RES.unit.* section.
*
* @param currentResidue
* Expected residue to be read
* @param br
* BufferedReader of current position in file
* @throws ParseException
* @throws IOException
*/
private void readResidueSection(Residue currentResidue, BufferedReader br)
throws ParseException, IOException {
String line;
String words[];
// Capture !entry.RESIDUE.unit.atoms line
line = br.readLine();
LOG.debug("Entering readResidueSection with "
+ currentResidue.toString());
LOG.debug("Entry line is " + line);
words = line.split("\\.");
// !entry , Resname, unit
LOG.debug(words[0] + " " + words[1] + " " + words[2]);
if (!(words[0].contentEquals("!entry"))) {
throw new ParseException("Expecting !entry term", 1);
}
if (!(words[2].contentEquals("unit"))) {
throw new ParseException("Expecting unit term", 1);
}
while (!(line = br.readLine()).matches("^!.*")) {
line = line.replace("\"", "");
words = line.split(" ");
Atom atom = new Atom(words[1], words[2],
Integer.parseInt(words[7]), Double.parseDouble((words[8])));
currentResidue.atoms.add(atom);
LOG.debug(line);
}
LOG.debug("!entry." + currentResidue + ".unit.atomspertinfo");
while (!(br.readLine()).matches("^!.*")) {
}
LOG.debug("!entry." + currentResidue + ".unit.boundbox");
while (!(br.readLine()).matches("^!.*")) {
}
LOG.debug("!entry." + currentResidue + ".unit.childsequence");
while (!(br.readLine()).matches("^!.*")) {
}
// TODO Parse into separate head and tail connect
LOG.debug("!entry." + currentResidue + ".unit.connect");
while (!(line = br.readLine()).matches("^!.*")) {
LOG.debug("Connect line is " + line);
words = line.split(" ");
LOG.debug(words[1]);
Integer IndexOfAtom = Integer.parseInt(words[1]);
if (IndexOfAtom.equals(0)) {
LOG.debug(currentResidue + " does not have a connect atom");
} else {
/*
* Offset done because Atoms within lib start from 1, whereas
* internally here, they start from 0.
*/
Integer OffsettedIndexOfAtom = IndexOfAtom - 1;
LOG.debug("OffsettedIndexOfAtom is " + OffsettedIndexOfAtom);
Atom connectAtom = currentResidue.getAtoms().get(
OffsettedIndexOfAtom);
connectAtom.setIndexWithinCurrentResidue(OffsettedIndexOfAtom);
currentResidue.externalBonds.add(connectAtom);
}
}
LOG.debug("!entry." + currentResidue + ".unit.connectivity");
while (!(line = br.readLine()).matches("^!.*")) {
LOG.debug("Line is " + line);
words = line.split(" ");
LOG.debug("bonds are " + words[1] + " " + words[2]);
/*
* Offset done because Atoms within lib start from 1, whereas
* internally here, they start from 0.
*/
Integer OffsettedIndexOfAtomI = Integer.parseInt((words[1])) - 1;
Integer OffsettedIndexOfAtomJ = Integer.parseInt((words[2])) - 1;
Atom i = currentResidue.getAtoms().get(OffsettedIndexOfAtomI);
Atom j = currentResidue.getAtoms().get(OffsettedIndexOfAtomJ);
i.setIndexWithinCurrentResidue(OffsettedIndexOfAtomI);
j.setIndexWithinCurrentResidue(OffsettedIndexOfAtomJ);
Connection connection = new Connection(i, j);
currentResidue.connections.add(connection);
}
LOG.debug("!entry." + currentResidue + ".unit.hierarch");
while (!(br.readLine()).matches("^!.*")) {
}
LOG.debug("!entry." + currentResidue + ".unit.name");
while (!(br.readLine()).matches("^!.*")) {
}
LOG.debug("!entry." + currentResidue + ".unit.positions");
while (!(br.readLine()).matches("^!.*")) {
}
LOG.debug("!entry." + currentResidue + ".unit.residueconnect");
while (!(br.readLine()).matches("^!.*")) {
}
LOG.debug("!entry." + currentResidue + ".unit.residues");
while (!(br.readLine()).matches("^!.*")) {
}
LOG.debug("!entry." + currentResidue
+ ".unit.residuesPdbSequenceNumber");
while (!(br.readLine()).matches("^!.*")) {
}
LOG.debug("!entry." + currentResidue + ".unit.solventcap");
while (!(br.readLine()).matches("^!.*")) {
}
LOG.debug("!entry." + currentResidue + ".unit.velocities");
/*
* This double check is done since this region can be the final resiude
* in a file
*/
while (((line = br.readLine()) != null) && (!line.matches("^!.*"))) {
/*
* Mark the end of this residue so that the next readResidueSection
* is in the correct place
*/
br.mark(BUFFER_SIZE);
}
}
/**
* Main parsing method
*
* @param ises
* InputStreams of LibFile
* @throws ParseException
* @throws IOException
*/
private void read(InputStream... ises) throws ParseException, IOException {
for (InputStream is : ises) {
if (is == null) {
throw new RuntimeException("Lib:read() Inputstream is null");
}
// read it with BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
// Check that we are actually reading a .lib file
if (!(br.readLine()).contentEquals("!!index array str")) {
throw new RuntimeException(
"This does not look like a .lib file");
}
/*
* Put in place a holder for residues that we expect to read given
* the preamble in the header section
*/
ArrayList<Residue> expectedResidues = new ArrayList<>();
// Populate expected residues from the !!index array str section
while (!(line = br.readLine()).matches("^!.*")) {
line = line.toUpperCase();
line = line.replace("\"", "");
line = line.trim();
LOG.debug(line);
Residue residue = new Residue(line);
expectedResidues.add(residue);
// Mark the final line of the !!index array str so that
// readResidueSection() can read the !entry.$RES.unit.atoms line
br.mark(BUFFER_SIZE);
}
/*
* TODO This is broken since this will expect residues read in from
* the first inputstream to be present in the second inputstream.
*/
for (Residue expectedResidue : expectedResidues) {
LOG.debug("");
LOG.debug("****************************");
LOG.debug("Parsing " + expectedResidue.getName());
// Move br back to the line !entry.$RES.unit.atoms
br.reset();
// Read each residue
readResidueSection(expectedResidue, br);
}
// Now add them to the main list
residues.addAll(expectedResidues);
}
}
/**
* Returns a Residue object in the Lib file as a function of Residue Name
*
* @param Name
* Identifier of the residue (i.e. CYS)
* @return Residue resulting from parsing
*/
public Residue getResidueByName(String Name) {
for (Residue residue : residues) {
if (residue.getName().equals(Name)) {
return residue;
}
}
return null;
}
public ArrayList<Residue> getResidues() {
return residues;
}
public ArrayList<Atom> getAtoms() {
ArrayList<Atom> atoms = new ArrayList<>();
for (Residue residue : residues) {
atoms.addAll(residue.getAtoms());
}
return atoms;
}
public Set<Atom> getUniqueAtoms() {
Set<Atom> atoms = new HashSet<>();
for (Residue residue : residues) {
atoms.addAll(residue.getAtoms());
}
return atoms;
}
/**
* Returns an atom, given its index in a set of AMBER lib files
*
* @param index
* Index of atom requested
* @return Atom corresponding to that index
*/
public Atom getAtomByIndex(Integer index) {
ArrayList<Atom> atoms = new ArrayList<>();
for (Residue residue : residues) {
atoms.addAll(residue.getAtoms());
}
return atoms.get(index);
}
/**
* Given an atom within the Lib file, return its index in lib.atoms
*
* @param atom
* Atom to be checked in lib file object
* @return Index of that atom in lib.atoms
*/
public Integer getAtomByIndex(Atom atom) {
ArrayList<Atom> atoms = new ArrayList<>();
for (Residue residue : residues) {
atoms.addAll(residue.getAtoms());
}
return atoms.indexOf(atom);
}
public void addFromMol2(Mol2 mol2) {
if (mol2 == null) {
throw new RuntimeException("addFromMol2() mol2 is null");
}
residues.add(mol2.getResidue());
}
}