/*
* Copyright (c) 2005–2012 Goethe Center for Scientific Computing - Simulation and Modelling (G-CSC Frankfurt)
* Copyright (c) 2012-2015 Goethe Center for Scientific Computing - Computational Neuroscience (G-CSC Frankfurt)
*
* This file is part of NeuGen.
*
* NeuGen is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* as published by the Free Software Foundation.
*
* see: http://opensource.org/licenses/LGPL-3.0
* file://path/to/NeuGen/LICENSE
*
* NeuGen 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.
*
* This version of NeuGen includes copyright notice and attribution requirements.
* According to the LGPL this information must be displayed even if you modify
* the source code of NeuGen. The copyright statement/attribution may not be removed.
*
* Attribution Requirements:
*
* If you create derived work you must do the following regarding copyright
* notice and author attribution.
*
* Add an additional notice, stating that you modified NeuGen. In addition
* you must cite the publications listed below. A suitable notice might read
* "NeuGen source code modified by YourName 2012".
*
* Note, that these requirements are in full accordance with the LGPL v3
* (see 7. Additional Terms, b).
*
* Publications:
*
* S. Wolf, S. Grein, G. Queisser. NeuGen 2.0 -
* Employing NeuGen 2.0 to automatically generate realistic
* morphologies of hippocapal neurons and neural networks in 3D.
* Neuroinformatics, 2013, 11(2), pp. 137-148, doi: 10.1007/s12021-012-9170-1
*
*
* J. P. Eberhard, A. Wanner, G. Wittum. NeuGen -
* A tool for the generation of realistic morphology
* of cortical neurons and neural networks in 3D.
* Neurocomputing, 70(1-3), pp. 327-343, doi: 10.1016/j.neucom.2006.01.028
*
*/
package org.neugen.parsers;
import java.io.FileInputStream;
import javax.xml.parsers.SAXParserFactory;
import org.neugen.datastructures.neuron.NeuronBase;
import org.neugen.datastructures.Section;
import org.neugen.datastructures.Segment;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import javax.vecmath.Point3f;
import org.apache.log4j.Logger;
import org.neugen.datastructures.AxonSection;
import org.neugen.datastructures.NetBase;
import org.neugen.datastructures.Cellipsoid;
import org.neugen.datastructures.DataStructureConstants;
import org.neugen.datastructures.Dendrite;
import org.neugen.datastructures.DendriteSection;
import org.neugen.datastructures.Net;
import org.neugen.datastructures.neuron.Neuron;
import org.neugen.datastructures.SectionLink;
/**
* @todo: setze den Sektionentyp!
*
* Read a neuroML file in xml format and create a neuronal net.
* @author Sergei Wolf
*/
public final class MorphMLReader extends XMLFilterImpl {
/** use to log messages */
private static final Logger logger = Logger.getLogger(MorphMLReader.class.getName());
// If getAncestorElement is called for non existent ancestors...
private static final String NULL_ELEMENT = "--- Null Element ---";
private Section currentSection;
private Segment currentSegment;
private Stack<String> elementStack;
private Neuron neuron;
private Net net;
//cable id: for neugen export files
private int currentSecId;
private Map<Integer, Section> sections;
private Map<Integer, Section> somaSec;
private Map<Integer, Section> axonSec;
private Map<Integer, Section> dendSec;
private Map<Integer, Segment> segments;
// diese Tabelle speichert zu jedem Segment die zugehörige Sektion-Id
private Map<Integer, Integer> segIdSecId;
/**
* @todo
* Soma kann auch aus mehreren Sektionen aufgebaut sein.
* Merke in der Liste die IDs der Sektionen.
* Wenn nicht alle Sektionen des Soma rausgeschrieben werden,
* dann braucht man eine Liste in Cellipsoid
*
*/
private List<Integer> somaSecIds;
private int contentProgress;
private NeuroMLReaderTask myTask;
/** total number of sections */
private int totalNumSecs;
/** total numbert of segments */
private int totalNumSegs;
public MorphMLReader() {
init();
}
public MorphMLReader(NeuroMLReaderTask task) {
myTask = task;
init();
}
public void init() {
contentProgress = 0;
totalNumSecs = 0;
totalNumSegs = 0;
currentSecId = 0;
sections = new HashMap<Integer, Section>();
somaSec = new HashMap<Integer, Section>();
axonSec = new HashMap<Integer, Section>();
dendSec = new HashMap<Integer, Section>();
segments = new HashMap<Integer, Segment>();
segIdSecId = new HashMap<Integer, Integer>();
somaSecIds = new ArrayList<Integer>();
elementStack = new Stack<String>();
}
public void setProgress(int textSize) {
if (myTask != null) {
contentProgress += textSize;
//System.out.println("ProgrtessBar: value " + contentProgress + ", " + myTask.fileLength);
if (contentProgress >= myTask.fileLength) {
contentProgress = myTask.fileLength;
}
myTask.setMyProgress(contentProgress, 0, myTask.fileLength);
}
}
public int getTotalNumSecs() {
return totalNumSecs;
}
public void incrementTotalNumSecs() {
totalNumSecs++;
}
public int getTotalNumSegs() {
return totalNumSegs;
}
public void incrementTotalNumSegs() {
totalNumSegs++;
}
public Net getNet() {
return net;
}
public void setCurrentElement(String newElement) {
elementStack.push(newElement);
}
public String getCurrentElement() {
return elementStack.peek();
}
public void stepDownElement() {
elementStack.pop();
}
public String getParentElement() {
return getAncestorElement(1);
}
/**
* Taking the child parent thing to it's logical extension...
* parent element is 1 generation back, parent's parent is 2 back, etc.
*/
public String getAncestorElement(int generationsBack) {
if (elementStack.size() < generationsBack + 1) {
return NULL_ELEMENT;
}
return elementStack.elementAt(elementStack.size() - (generationsBack + 1));
}
/**
* wird immer aufgerufen, wenn Zeichen im Dokument auftauchen
*
* @param ch Charakter Array
* @param start Startindex der Zeichen in ch
* @param length Länge der Zeichenkette
*/
@Override
public void characters(char[] ch, int start, int length) {
String contents = new String(ch, start, length);
if (contents.trim().length() > 0) {
//System.out.print(getCurrentElement());
String metaData = MetadataConstants.GROUP_ELEMENT;
if (getCurrentElement().equals(metaData)) {
Section sec = sections.get(currentSecId);
//System.out.println("Die Meta und ID: " + currentSecId + ", " + contents);
//this.sections.remove(currentSecId);
if (contents.equals(DataStructureConstants.AXONAL_GROUP)) {
axonSec.put(currentSecId, sec);
} else if (contents.equals(DataStructureConstants.DENDRITIC_GROUP)) {
dendSec.put(currentSecId, sec);
} else if (contents.equals(DataStructureConstants.SOMA_GROUP)) {
somaSec.put(currentSecId, sec);
} else {
sections.put(currentSecId, sec);
}
}
}
setProgress(length);
}
@Override
public void startDocument() {
net = new NetBase();
}
@Override
public void endDocument() {
//System.out.println("ProgressBar: " + contentProgress);
//System.out.println("FileLength: " + myTask.fileLength);
//setProgress(myTask.fileLength);
int i = 0;
for (Neuron n : net.getNeuronList()) {
n.setIndex(i);
i++;
}
if (myTask != null) {
myTask.setMyProgress(myTask.fileLength, 0, myTask.fileLength);
}
}
/**
* wird bei jedem öffnenden Tag aufgerufen
* bei leerem Tag wird statElement und endElement direkt hintereinander
* aufgerufen
*
* @param namespaceURI URI des Namespaces für diese Element, kann auch ein leerer String sein
* @param localName Lokaler Name des Elements, kann auch ein leerer String sein
* @param qName Qualifizierter Name des Elements: cells,cell,segments,segment usw.
* @param attributes Liste der Attribute
* @throws org.xml.sax.SAXException
*/
@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes attributes) throws SAXException {
setProgress(localName.length() + namespaceURI.length() + qName.length() + 2);
for (int i = 0; i < attributes.getLength(); i++) {
String attName = attributes.getLocalName(i);
setProgress(attName.length());
}
//System.out.println("Member: startElement");
// lege den Tag Namen auf den Stack
setCurrentElement(localName); //cells,cell,segments,segment,usw.
// wenn der Namen Cell ist, dann erzeuge eine Zelle
// <cell name="soma_0">
if (getCurrentElement().equals(MorphMLConstants.CELL_ELEMENT)) //cell
{
String name = attributes.getValue(MorphMLConstants.CELL_NAME_ATTR); //name
neuron = new NeuronBase();
neuron.setName(name);
} //wenn das Tag ein Segment ist
//<segment id="1541" name = "Seg1_axon_0" parent="1540" cable = "197">
else if (getCurrentElement().equals(MorphMLConstants.SEGMENT_ELEMENT)) //segment
{
String id = attributes.getValue(MorphMLConstants.SEGMENT_ID_ATTR); //id
String name = attributes.getValue(MorphMLConstants.SEGMENT_NAME_ATTR); //name
String sectionId = attributes.getValue(MorphMLConstants.SEGMENT_CABLE_ID_ATTR); //cable
Segment segment = new Segment();
segment.setId(Integer.parseInt(id));
segment.setName(name);
incrementTotalNumSegs();
Section section;
Integer sectionIdInteger = new Integer(sectionId);
if (sections.containsKey(sectionIdInteger)) {
section = sections.get(sectionIdInteger);
} else {
section = new Section();
section.setId(Integer.parseInt(sectionId));
sections.put(sectionIdInteger, section);
incrementTotalNumSecs();
//logger.info("new Section: " + section.getId());
}
Integer segmentIdInteger = new Integer(id);
segments.put(segmentIdInteger, segment);
segIdSecId.put(segmentIdInteger, sectionIdInteger);
currentSegment = segment;
section.getSegments().add(segment);
//logger.info("newSeg: " + name + " added to " + section.getId());
String parent = attributes.getValue(MorphMLConstants.SEGMENT_PARENT_ATTR); //parent
if (parent != null) {
try {
//logger.info("parent: " + parent);
Segment parentSegment = segments.get(new Integer(parent));
parentSegment.getName(); // Just to check for null pointer...
segment.setParent(parentSegment);
int parentSegId = parentSegment.getId();
Integer parentSectionId = segIdSecId.get(parentSegId);
//System.out.println("Debug start");
// nur bei unterschiedlichen Sektionen.
if (!parentSectionId.equals(sectionIdInteger)) {
//System.out.println("section and parent section id: " + sectionId + " and " + parentSectionId );
//Sektionen verbinden
Section parentSection = sections.get(parentSectionId);
if (parentSection.getChildrenLink() == null) {
SectionLink childrenLink = new SectionLink();
childrenLink.setParental(parentSection);
childrenLink.setBranch0(section);
parentSection.setChildrenLink(childrenLink);
section.setParentalLink(childrenLink);
childrenLink.setParental(parentSection);
childrenLink.getChildren().add(section);
} else {
SectionLink childrenLink = parentSection.getChildrenLink();
if (childrenLink.getBranch0() == null) {
childrenLink.setBranch0(section);
childrenLink.getChildren().add(section);
} else if (childrenLink.getBranch1() == null) {
childrenLink.setBranch1(section);
childrenLink.getChildren().add(section);
}
if (section.getParentalLink() == null) {
section.setParentalLink(childrenLink);
}
}
}
//System.out.println("Debug end");
//welche id hat der Parent
//System.out.println("segID, segPID: " + parent + ", " + parentId);
//System.out.println("segmentId, parentSegId: " + segmentIdInteger + ", " + parentId);
} catch (Exception e) {
logger.error("problem location parent of: " + parent, e);
}
} else {
logger.debug("Section has no parent: " + section.getId());
}
//System.out.println("Segment gefunden-end");
} //<proximal x="0" y="0" z="10.68" diameter="10.68"/>
else if (getCurrentElement().equals(MorphMLConstants.SEGMENT_PROXIMAL_ELEMENT)) //proximal
{
//System.out.println("Segment proximal");
String xVal = attributes.getValue(MorphMLConstants.POINT_X_ATTR); //x
String yVal = attributes.getValue(MorphMLConstants.POINT_Y_ATTR); //y
String zVal = attributes.getValue(MorphMLConstants.POINT_Z_ATTR); //z
String diamVal = attributes.getValue(MorphMLConstants.POINT_DIAM_ATTR); //diameter
currentSegment.setStart(xVal, yVal, zVal, diamVal);
//this.currentSegment.set_segment(sstart, send, sradiusstart, sradiusend);
//TODO speichere die Daten in die Hash : id + segmentData
//System.out.println("Segment proximal-end");
} //<distal x="0" y="0" z="0" diameter="10.68"/>
else if (getCurrentElement().equals(MorphMLConstants.SEGMENT_DISTAL_ELEMENT)) //distal
{
String xVal = attributes.getValue(MorphMLConstants.POINT_X_ATTR); //x
String yVal = attributes.getValue(MorphMLConstants.POINT_Y_ATTR); //y
String zVal = attributes.getValue(MorphMLConstants.POINT_Z_ATTR); //z
String diamVal = attributes.getValue(MorphMLConstants.POINT_DIAM_ATTR); //diameter
currentSegment.setEnd(xVal, yVal, zVal, diamVal);
} //<cable id = "0" name = "soma_0" fractAlongParent = "0"/>
else if (getCurrentElement().equals(MorphMLConstants.CABLE_ELEMENT) //cable
&& getAncestorElement(1).equals(MorphMLConstants.CABLES_ELEMENT)) {
String id = attributes.getValue(MorphMLConstants.CABLE_ID_ATTR); //id
String name = attributes.getValue(MorphMLConstants.CABLE_NAME_ATTR); //name
Section section = null;
Integer sectionIdInteger = new Integer(id);
section = sections.get(sectionIdInteger);
if (section == null) {
String mes = "Problem parsing MorphML file. Section/cable " + name
+ " does not refer to one referenced in the list of segments";
logger.error(mes);
throw new SAXException(mes);
}
section.setName(name);
section.setId(Integer.parseInt(id));
// notwendig, denn in der Funktion End Element werden wir auf die Section wieder zugreifen
currentSection = section;
currentSecId = sectionIdInteger;
//TODO fractAlongParent is not implementet
}
//System.out.println("Member: startElement- end");
}
/**
* wird bei jedem schließenden Tag aufgerufen,
*
* @param namespaceURI URI des Namespaces für dieses Element, kann auch ein leerer String sein
* @param localName Lokaler Name des Elements, kann auch ein leerer String sein
* @param qName Qualifizierter Name des Elements
*/
//TODO: Axon, Soma und Dendriten erzeugen!!
@Override
public void endElement(String namespaceURI, String localName, String qName) {
// cell end
if (getCurrentElement().equals(MorphMLConstants.CELL_ELEMENT)) {
net.getNeuronList().add(neuron);
net.setTotalNumOfSegments();
/*myTask.ngView.outPrintln("name of neuron: " + neuron.getInstanceName());
myTask.ngView.outPrintln("soma: ");
myTask.ngView.outPrintln(" number of sections: " + neuron.getSoma().getSomaSections().size());
myTask.ngView.outPrintln(" number of segments: " + neuron.getSoma().getNumberOfSomaSegments());
myTask.ngView.outPrintln("axon: ");
myTask.ngView.outPrintln(" number of sections: " + neuron.getAxon().getNumberOfSections());
myTask.ngView.outPrintln(" number of segments: " + neuron.getAxon().getNumberOfSegments());
myTask.ngView.outPrintln("dendrites: " + neuron.getDenList().size());
myTask.ngView.outPrintln(" number of sections: " + neuron.getNumberOfAllDenSections());
myTask.ngView.outPrintln(" number of segments: " + neuron.getNumberOfAllDenSegments());
myTask.ngView.outPrintln();*/
//neuron.infoNeuron();
//myTask.setTaskOutput(neuron.getInstanceName() + "\n");
//taskOutput.setCaretPosition(taskOutput.getText().length());
} else if (getCurrentElement().equals(MorphMLConstants.CABLE_ELEMENT) && getAncestorElement(1).equals(MorphMLConstants.CABLES_ELEMENT)) {
String somaName = neuron.getName().toLowerCase();
String currentSectionName = currentSection.getName().toLowerCase();
//Soma end
if (currentSectionName.indexOf("soma") >= 0 || currentSectionName.indexOf(somaName) >= 0) {
somaSecIds.add(currentSecId); //speichere die Sektionen des Soma in die Liste
if (neuron.getSoma().getCylindricRepresentant() == null) {
Cellipsoid soma = neuron.getSoma();
List<Segment> segList = currentSection.getSegments();
int nsegs = segList.size();
if (nsegs >= 1) {
Segment firstSeg = segList.get(0);
Point3f mid = firstSeg.getCenter();
if (nsegs > 1) {
Segment lastSeg = segList.get(nsegs - 1);
//mid = Fork.add(firstSeg.getStart(), lastSeg.getEnd(), d);
mid.add(firstSeg.getStart(), lastSeg.getEnd());
mid.scale(0.5f);
}
soma.setMid(mid);
}
float radius = 0.0f;
for (Segment segment : segList) {
if (segment.getStartRadius() > radius) {
radius = segment.getStartRadius();
}
if (segment.getEndRadius() > radius) {
radius = segment.getEndRadius();
}
}
Point3f sradii = new Point3f(radius, radius, radius);
soma.changeRadii(sradii);
soma.setCylindricRepresentant(currentSection);
soma.getSections().add(currentSection);
logger.info("Soma: " + currentSection.getName());
} else {
//logger.info("Soma hat noch eine Sektion: " + currentSection.getName());
// Segment firstSeg = currentSection.getSegments().get(0);
//firstSeg.printSegment();
//soma.getCylindricRepresentant().setChildren(children);
//neu, um alle Sektionen des Somas zu durchlaufen
neuron.getSoma().getSections().add(currentSection);
}
} //Axon end
else if (currentSectionName.indexOf("axon") >= 0) {
SectionLink parentLink = currentSection.getParentalLink();
if (parentLink != null) {
if (parentLink.getParental() != null) {
int parentID = parentLink.getParental().getId();
if (somaSecIds.contains(parentID)) {
logger.info("axon ist an soma angeschlossen!");
AxonSection axonSection = new AxonSection(currentSection);
neuron.getAxon().setFirstSection(axonSection);
// System.out.println("Axon: " + currentSection.getSectionName() + ", TypeID: "
// + currentSection.getSectionId() + ", an Soma");
//Segment firstSeg = currentSection.getSegments().get(0);
//firstSeg.printSegment();
}
}
} else {
logger.info("Axon has no parent! Set first section: " + currentSection.getId());
logger.info("number of segments: " + currentSection.getSegments().size());
AxonSection axonSection = new AxonSection(currentSection);
neuron.getAxon().setFirstSection(axonSection);
}
} /*
else if(currentSectionName.indexOf("dendrite") >=0
|| currentSectionName.indexOf("apical") >=0
|| currentSectionName.indexOf("basal") >=0
|| currentSectionName.indexOf("user") >=0)
*/ //alle anderen Sektionen
else {
if (!getCurrentElement().equals(MorphMLConstants.CELL_ELEMENT)) {
// System.out.println("localName: " + localName);
// System.out.println("Dendrite Sektion: " + currentSection.getSectionName());
// if (soma.getCylindricRepresentant().getSectionId() == currentSection.getParental().parental.getSectionId()) {
SectionLink parentLink = currentSection.getParentalLink();
if (parentLink != null) {
if (parentLink.getParental() != null) {
int parentID = parentLink.getParental().getId();
if (somaSecIds.contains(parentID)) {
logger.info("parent found! (dendrite)");
DendriteSection denSection = new DendriteSection(currentSection);
Dendrite dendrite = new Dendrite();
dendrite.setFirstSection(denSection);
neuron.getDendrites().add(dendrite);
logger.info("Dendrit: " + currentSection.getName()
+ ", secID: " + currentSection.getId() + ", an Soma");
}
}
} else {
DendriteSection denSection = new DendriteSection(currentSection);
Dendrite dendrite = new Dendrite();
dendrite.setFirstSection(denSection);
neuron.getDendrites().add(dendrite);
}
}
}
}
stepDownElement();
setProgress(qName.length() + 2);
}
public void runMorphMLReader(String fname) {
try {
File f = new File(fname);
logger.info("Pfad: " + f.getAbsolutePath());
FileInputStream instream = null;
InputSource is = null;
//neuen Sax Parser erzeugen
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
XMLReader xmlReader = spf.newSAXParser().getXMLReader();
//MorphMLReader mmlBuilder = new MorphMLReader();
xmlReader.setContentHandler(this);
instream = new FileInputStream(f);
is = new InputSource(instream);
xmlReader.parse(is);
logger.info("Total number of segments: " + getTotalNumSegs());
logger.info("Total number of sections: " + getTotalNumSecs());
} catch (Exception e) {
logger.error("Fehler in der Main-Funktion: " + e, e);
}
}
//TODO Meta group soll noch imlementiert werden!
public static void main(String args[]) {
//System.out.println(MorphMLReader.class.toString());
String cA1_1 = "CA1GatingModelView.xml";
String CA1_2 = "CA1ModelView.xml";
String CA1_3 = "CA1XiaoModelView.xml";
//File f = new File("goodCells/CA1MagicalModelView.xml");
//File f = new File("goodCells/CA1LamotrigineModelView.xml");
//File f = new File("CA1Migliore.xml");
MorphMLReader mmlBuilder = new MorphMLReader();
mmlBuilder.runMorphMLReader(CA1_3);
//System.out.println(MorphMLReader.class.toString() + " -end+");
}
}