next up previous contents
Nächste Seite: Speicherung der Atome zur Aufwärts: Implementierung Vorherige Seite: Implementierung   Inhalt


Die Klasse PDBHandler


Instanziierung eines Document-Objekts
DocumentBuilderFactory factory = 
    DocumentBuilderFactory.newInstance();	
DocumentBuilder builder = null;
try {
    builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e){
    System.err.println ("ParserConfigurationException  "
        + e.getMessage());
    System.exit (1);
}
		
DOMImplementation domIm = builder.getDOMImplementation();
try {
    DocumentType docType = domIm.createDocumentType
       ("molecule", "http://www.xml-cml.org/dtd/cml1_0_1.dtd",
        "file:./cml.dtd");
    document = domIm.createDocument("cml", "molecule", docType);

    Element moleculeNode = (Element) document.getLastChild();
    Element atomArray = document.createElement("atomArray");
    atomArray.setAttribute("id", "Model 1");
    moleculeNode.appendChild(atomArray);

} catch (DOMException e) {
    System.err.println ("DOMException  " + e.getMessage());
    System.exit (1);
}

In dieser Klasse ist die konkrete Konvertierung implementiert, die mit der Methode convert() gestartet wird. Als Rückgabewert liefert die Methode eine Document-Instanz, die zuvor im Konstruktor angelegt und initialisiert wurde. Dies ist im Quellcode 6.3 abgebildet. Eine Instanz der Klasse DocumentBuilderFactory erzeugt das Objekt DocumentBuilder, das seinerseits wiederum eine DOMImplementation-Instanz erzeugt. Erst mit Hilfe der Methode createDocument() wird eine Instanz der Document-Klasse angelegt. Entscheidend ist dabei, daß in der zuvor angelegten Dokumenttypdeklaration der Name des Wurzelelements angegeben wird, in diesem Fall der Name molecule. Anschließend wird das einführende CML-Element atomArray in den DOM-Tree an die passende Stelle eingefügt. In der Methode convert() wird die Ausgangsdatei über die BufferedReader-Instanz zeilenweise eingelesen und interpretiert. Da ein Schlüsselwort jede Zeile des PDB-Formats einleitet, ist es naheliegend, die Methode, die eine bestimmte Zeilenart interpretiert, ebenfalls so zu bezeichnen. Jede dieser Methoden wird wiederum mit den Methoden des java.lang.reflect-Pakets dynamisch aufgerufen. Dadurch wird ebenfalls die Erweiterbarkeit vereinfacht. Möchte man eine weitere Zeilenart interpretieren, muß lediglich die entsprechende Methode implementiert werden. Zu beachten ist dabei, daß jeder Methode der gleiche Parameter übergeben wird: ein String-Objekt, in dem eine Zeile des chemischen Formats abgelegt ist. Insgesamt werden fünf verschiedene Zeilen verarbeitet.

atom()
Durch eine ATOM-Zeile wird jedes Atom einer Standardgruppe individuell beschrieben. Der genaue Aufbau der Zeile wurde in Kapitel 4.1.2 erläutert. Mit dieser Methode werden zweierlei Aufgaben erfüllt. Die einzelnen Atomdaten werden zum Einen direkt in den DOM-Tree eingefügt und zum Anderen werden sie zur späteren Berechnung der Bindungen zwischengespeichert.

Verarbeitung einer ATOM-Zeile
public void atom(String line) {	
  // auslesen der Zeileninformationen
  String atomId = line.substring(6, 11);
  String atomName = line.substring(12, 16);
  String resName = line.substring(17, 20);
  String chainId = line.substring(21, 22);
  String resId = line.substring(22, 26);
  String xString = line.substring(30, 38).trim();
  String yString = line.substring(38, 46).trim();
  String zString = line.substring(46, 54).trim();
  String occupancy = line.substring(54, 60).trim();	
  // ermittelt das Elementsymbol
  String symbol = atomName.substring(0, 2).trim();
  if (Character.isDigit(symbol.charAt(0))) symbol = "H";
  // eindeutiger String mit zusaetzlichen Informationen
  String id = atomId +":"+ atomName +":"+ resId +
     ":"+ resName +":"+ chainId;
  Element atomNode = document.createElement("atom");
  atomNode.setAttribute("title", atomName);
  atomNode.setAttribute("id", id);

  makeNode("string","builtin","elementType",symbol,atomNode);
  makeNode("float","builtin","x3",xString, atomNode);
  makeNode("float","builtin","y3",yString, atomNode);
  makeNode("float","builtin","z3",zString, atomNode);
  makeNode("float","builtin","occupancy",occupancy,atomNode);

  Element atomArray =(Element)
    ((Element) document.getLastChild()).getLastChild();
  atomArray.appendChild(atomNode);

  Atom atom = null;  // Container fuer die Atomdaten
  try { atom = new Atom(pse.getElement(symbol)); } 
  catch (NoSuchElementException e) {
    System.err.println("Kein Element "+symbol+" gefunden!");
    System.exit(1);
  }
  atom.setX(xString); atom.setY(yString); atom.setZ(zString);
  atom.setId(id);
  if (line.substring(0, 6).trim().equals("ATOM"))
    atom.setStandard("true");
  else atom.setStandard("false");
  // Atom speichern, um spaeter die Bindungen zu berechnen
  // und auf die Daten zugreifen zu koennen
  residueBox.storeAtom(atom); atomTable.put(atomId.trim(),atom);	
}

Zunächst werden jedoch die einzelnen Informationen des Atoms aus der entsprechenden Zeile bestimmt, wie z.B. der Atomname oder die Proteinketten-Id. Da die Angabe des Elementsymbols in den Spalten 77-78 nicht zuverlässig ist, wird das Symbol aus dem Atomnamen konstruiert. Die ersten beiden Buchstaben bilden hier in der Regel das Symbol. Um im weiteren Verlauf ein Atom genauer charakterisieren zu können, wird eine Variable id angelegt, in der verschiedene charakteristische Informationen verarbeitet sind: atomId, atomName, resId, resName und chainId. Im nächsten Schritt werden alle wichtigen Informationen des Atoms in den DOM-Tree eingefügt. Dafür wird zunächst ein atom-Element erzeugt, dem mit der Methode setAttribute() die beiden Attribute title und id zugeordnet werden. Der Wert des Attributs title ist der Atomname, wie er in der PDB-Datei verwendet wird, und der Wert des id-Attributs entspricht der id Variable. Dadurch läßt sich ein Atom eindeutig identifizieren und kann zusätzlich weitere Informationen erhalten. Bevor das atom-Element mit der Methode appendchild() unter dem Element atomArray in den DOM-Tree eingefügt wird, werden mit der Methode makeNode() alle wichtigen Informationen des Atoms unter dem Element angehangen.
Im abschließenden Schritt werden die Atominformationen, im Wesentlichen die Koordinaten und die Id, in einem Atom-Objekt gespeichert. Des Weiteren wird festgehalten, ob das Atom Teil einer Nicht-Standard- oder einer Standardgruppe ist. Zudem enthält das Objekt alle spezifischen Elementinformationen, da dem Konstruktor das entsprechende Element-Objekt, das zuvor mit einer Instanz der Klasse PSE erzeugt wurde, als Parameter übergeben wird. Das Atom-Objekt wird dann in einem ResidueContainer-Objekt gespeichert, um später die Bindungen untereinander berechnen zu können.
Da Atome im PDB-Format innerhalb einer CONECT-Zeile nur durch ihre Nummern symbolisiert werden, müssen die Daten des Atoms über die Atomnummer abrufbar sein. Deshalb wird jedes Atom-Objekt in einer Hashtabelle atomTable abgelegt, wobei die Atomnummer den Schlüssel zu jedem Objekt bildet.

hetatm()
Durch eine HETATM-Zeile wird ein Atom einer Nicht-Standardgruppe beschrieben. Der Aufbau der Zeile ist im Vergleich zu einer ATOM-Zeile bis auf das einleitende Schlüsselwort identisch. Deshalb liegt es nahe, die schon vorhandene atom()-Methode zu nutzen. Der einzige Unterschied liegt darin, daß das Atom als Teil einer Nicht-Standardgruppe gekennzeichnet wird. Bei der späteren Berechnung der Bindungen spielt das eine wesentliche Rolle.
conect()
Die Bindungen innerhalb eines Proteins werden mit einer CONECT-Zeile angegeben, was im Kapitel 4.1.3 genauer beschrieben wurde. Ist beim Aufruf die Flagge -c gesetzt, werden nur die Bindungen, die in den entsprechenden CONECT-Zeilen angegeben werden, für die Nicht-Standardgruppen verwendet. Eine weitere Berechnung findet für diese Gruppen nicht statt. Dabei werden nur normale Bindungen betrachtet. Wasserstoffbrücken und Salzbrücken werden vernachlässigt. Dazu werden zuerst die Datenfelder ausgelesen, in denen die Atomnummer der gebundenen Atome abgelegt sind. Mit der Methode createBond() wird dann versucht eine entsprechende Bindung in den DOM-Tree einzufügen.

Verarbeitung einer CONECT-Zeile
public void conect(String line) {
    // ueberprueft, ob die Flagge -c gesetzt wurde
    // wenn nein wird die Zeile ignoriert
    if (conect.booleanValue()) {
        // auslesen der Daten
        String mainAtom = line.substring(6, 11).trim();
        String bondedAtom_1 = line.substring(11, 16).trim();
        String bondedAtom_2 = line.substring(16, 21).trim();
        String bondedAtom_3 = line.substring(21, 26).trim();
        String bondedAtom_4 = line.substring(26, 31).trim();
        // versucht entsprechende Bindung anzulegen
        createBond(mainAtom, bondedAtom_1);
        createBond(mainAtom, bondedAtom_2);
        createBond(mainAtom, bondedAtom_3);
        createBond(mainAtom, bondedAtom_4);
    }
}

model()
Mit der MODEL-Zeile werden verschiedene Modelle eines Proteins in einer Datei verwaltet. Besonders bei der NMR-Spektroskopie18 ergeben sich mehrere Modelle eines Proteins. In der Zeile wird auch die Nummer des Modells abgelegt. Wird dort eine Nummer größer als 1 gefunden, wird ein neues atomArray-Element in den DOM-Tree eingefügt. Da zudem die Bindungsverhältnisse in jedem Modell identisch sind, werden die im ResidueContainer-Objekt gesammelten Atomdaten gelöscht.

Verarbeitung einer MODEL-Zeile
public void model(String line) {
    // Nummer des Modells
    String modelCounter = line.substring(10, 14).trim();
    // ist Nummer > 1 werden neue Knoten
    // in den DOM-Tree eingehangen
    if (!modelCounter.equals("1")) {

       Element atomArray = document.createElement("atomArray");
       atomArray.setAttribute("id", "Model " + modelCounter);

       // ermittelt passende Position des neuen Knotens
       // und haengt ihn ein
       Element moleculeNode = (Element) document.getLastChild();
       moleculeNode.appendChild(atomArray);

       // loescht bisherige Atomdaten, da Bindungsverhaeltnisse
       // aller Modelle gleich sind
       residueBox.clear();
    }
}

end()
Mit der Zeile END schließt jede PDB-Datei ab. Zu dem Zeitpunkt, als die Zeile eingelesen worden ist, ist also die Verarbeitung aller Atome abgeschlossen. Da nun die Koordinaten aller Atome bekannt sind, lassen sich nun die Bindungen untereinander bestimmen. Realisiert wird das mit der Methode makeBonds() der Klasse ResidueContainer. Sie liefert einen Vektor zurück, in dem alle berechneten Bindungen als Bond-Objekte abgelegt werden. Bevor die Bindungen in den DOM-Tree eingefügt werden, muß zunächst das Element bondArray im Baum platziert werden. Danach werden die Bindungen mit der Methode createBonds() in den DOM-Tree eingefügt.

Verarbeitung einer END-Zeile
public void end(String line) {
	
    // startet die Berechnung der Bindungen
    Vector bondBox = residueBox.makeBonds(conect);
    
    // bondArray in den DOM-Tree einfuegen
    Element moleculeNode = (Element) document.getLastChild();
    Element bondArray = document.createElement("bondArray");
    moleculeNode.appendChild(bondArray);
	
    for (int i = 0; i < bondBox.size(); i++) {
        Bond bond = (Bond) bondBox.elementAt(i);
        createBond(bond);
    }
}

Zum Abschluß ist in Abbildung 6.6 ein Sequenzdiagramm zu sehen, in dem nochmals die Interaktion zwischen den verschiedenen Objekten abgebildet ist. Dabei wird der Ablauf von der Klasse Konverter bis hin zum Document-Objekt skizziert. Über die Klasse Konverter wird eine Instanz der Klasse PDBHandler instanziiert, in der mit den Methoden atom() und end() die Konvertierung realisiert wird. Die Methoden hetatm(), model() und conect() werden zur Vereinfachung weggelassen. Auf die Berechnung der Bindungen mit den Methoden storeAtom() und makeBonds() wird im weiteren Verlauf des Kapitels näher eingegangen.

Abbildung 6.6: Sequenzdiagramm zur Ablaufsteuerung
\resizebox*{0.9\textwidth}{!}{\includegraphics{pics/sequenz}}


next up previous contents
Nächste Seite: Speicherung der Atome zur Aufwärts: Implementierung Vorherige Seite: Implementierung   Inhalt
Oliver Krone 2003-04-28