package org.nexml.model.impl;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.nexml.model.CategoricalMatrix;
import org.nexml.model.ContinuousMatrix;
import org.nexml.model.Document;
import org.nexml.model.Matrix;
import org.nexml.model.MolecularMatrix;
import org.nexml.model.OTUs;
import org.nexml.model.TreeBlock;
import org.w3c.dom.Element;
public class DocumentImpl extends AnnotatableImpl implements Document {
private List<OTUs> mOtusList = new ArrayList<OTUs>();
private List<Matrix<?>> mMatrixList = new ArrayList<Matrix<?>>();
private List<TreeBlock> mTreeBlockList = new ArrayList<TreeBlock>();
public static Collection<String> characterNames = new ArrayList<String>(); // XXX THIS IS WRONG!
/**
* Protected constructors that take a DOM document object but not
* an element object are used for generating new element nodes in
* a NeXML document. On calling such constructors, a new element
* is created, which can be retrieved using getElement(). After this
* step, the Impl class that called this constructor would still
* need to attach the element in the proper location (typically
* as a child element of the class that called the constructor).
* @param document a DOM document object
* @author rvosa
*/
public DocumentImpl(org.w3c.dom.Document document) {
super(document);
setRootAttributes();
}
/**
* This is the only public constructor that takes an element as
* its argument. This is so that we can start the recursive
* element traversal from outside this packages, e.g. in the
* DocumentFactory.
* @param document the containing DOM document object. Every Impl
* class needs a reference to this so that it can create DOM element
* objects
* @param element the <nex:nexml/> root element
* @author rvosa
*/
public DocumentImpl(org.w3c.dom.Document document, Element element) {
super(document, element);
if ( ! element.getOwnerDocument().equals(document) ) {
throw new RuntimeException("This'll never work");
}
List<Element> oTUsElements = getChildrenByTagName(element, OTUsImpl.getTagNameClass());
for (Element thisElement : oTUsElements) {
OTUsImpl otus = new OTUsImpl(document, thisElement);
mOtusList.add(otus);
}
List<Element> treeBlockElements = getChildrenByTagName(element, TreeBlockImpl.getTagNameClass());
for (Element treeBlockElement : treeBlockElements) {
String oTUsId = treeBlockElement.getAttribute("otus");
TreeBlockImpl treeBlock = new TreeBlockImpl(document,treeBlockElement,getOTUsById(oTUsId));
mTreeBlockList.add(treeBlock);
}
characterNames.clear();
List<Element> charsBlockElements = getChildrenByTagName(document
.getDocumentElement(), MatrixImpl.getTagNameClass());
for (Element charsBlock : charsBlockElements) {
List<Element> formatElements = getChildrenByTagName(charsBlock, "format");
for (Element thisE : formatElements) {
List<Element> charElements = getChildrenByTagName(thisE, "char");
for (Element charElement : charElements){
String charLabel = charElement.getAttribute("label").trim();
characterNames.add(charLabel.trim());
}
}
String xsiType = charsBlock.getAttribute(XSI_TYPE);
Matrix<?> matrix = null;
xsiType = xsiType.replaceAll("Seqs", "Cells");
charsBlock.setAttribute(XSI_TYPE, xsiType);
if (xsiType.indexOf("Continuous") > 0) {
matrix = new ContinuousMatrixImpl(getDocument(), charsBlock,
(OTUsImpl)getOTUsById(charsBlock.getAttribute("otus")));
}
else if ( xsiType.indexOf(MolecularMatrixImpl.DNA) > 0 ) {
matrix = new MolecularMatrixImpl(getDocument(), charsBlock,
(OTUsImpl)getOTUsById(charsBlock.getAttribute("otus")),
MolecularMatrixImpl.DNA
);
}
else if ( xsiType.indexOf(MolecularMatrixImpl.RNA) > 0 ) {
matrix = new MolecularMatrixImpl(getDocument(), charsBlock,
(OTUsImpl)getOTUsById(charsBlock.getAttribute("otus")),
MolecularMatrixImpl.RNA
);
}
else if ( xsiType.indexOf(MolecularMatrixImpl.Protein) > 0 ) {
matrix = new MolecularMatrixImpl(getDocument(), charsBlock,
(OTUsImpl)getOTUsById(charsBlock.getAttribute("otus")),
MolecularMatrixImpl.Protein
);
}
else {
matrix = new CategoricalMatrixImpl(getDocument(), charsBlock,
(OTUsImpl)getOTUsById(charsBlock.getAttribute("otus")));
}
mMatrixList.add(matrix);
}
}
private void setRootAttributes() {
getElement().setAttribute("version", DEFAULT_VERSION);
getElement().setAttribute("generator", getClass().getName());
getElement().setPrefix(NEX_PRE);
getElement().removeAttribute("id");
getElement().setAttribute( XMLNS_PRE, DEFAULT_NAMESPACE);
getElement().setAttributeNS( XMLNS_URI, XMLNS_PRE + ":" + NEX_PRE, DEFAULT_NAMESPACE );
getElement().setAttributeNS( XMLNS_URI, XMLNS_PRE + ":" + XSI_PRE, XSI_URI );
getElement().setAttributeNS( XMLNS_URI, XMLNS_PRE + ":" + XSD_PRE, XS_URI + "#" );
getElement().setAttributeNS( XMLNS_URI, XMLNS_PRE + ":" + RDF_PRE, RDF_URI + "#" );
}
protected DocumentImpl() {
}
/**
* This method creates an otus element and appends it to the document root.
*
* @author rvosa
*/
public OTUs createOTUs() {
OTUsImpl otus = new OTUsImpl(getDocument());
mOtusList.add(otus);
getElement().appendChild(otus.getElement());
return otus;
}
/**
* This method creates a trees element and appends it to the document root.
* Because NeXML requires that trees elements have an id reference attribute
* to specify the otus element it refers to, the equivalent OTUs object
* needs to be passed in here.
*
* @author rvosa
*/
public TreeBlock createTreeBlock(OTUs otus) {
TreeBlockImpl treeBlock = new TreeBlockImpl(getDocument());
mTreeBlockList.add(treeBlock);
getElement().appendChild(treeBlock.getElement());
treeBlock.setOTUs(otus);
return treeBlock;
}
/*
* (non-Javadoc)
* @see org.nexml.model.impl.NexmlWritableImpl#getTagName()
*/
@Override
String getTagName() {
return "nexml";
}
/**
* This method creates the characters element and appends it to the document
* root. Because NeXML requires that characters elements have an id
* reference attribute to specify the otus element it refers to, the
* equivalent OTUs object needs to be passed in here. In addition,
* characters elements need to specify the concrete subclass they implement
* (the xsi:type business). XXX Here, this subclass is set to StandardCells.
* Hopefully we come up with a better way to do this.
*
* @author rvosa
*/
public CategoricalMatrix createCategoricalMatrix(OTUs otus) {
CategoricalMatrixImpl categoricalMatrix = new CategoricalMatrixImpl(
getDocument());
mMatrixList.add(categoricalMatrix);
getElement().appendChild(categoricalMatrix.getElement());
categoricalMatrix.setOTUs(otus);
categoricalMatrix.getElement().setAttributeNS(XSI_URI, XSI_TYPE, NEX_PRE + ":StandardCells");
return categoricalMatrix;
}
/**
* This method creates the characters element and appends it to the document
* root. Because NeXML requires that characters elements have an id
* reference attribute to specify the otus element it refers to, the
* equivalent OTUs object needs to be passed in here. In addition,
* characters elements need to specify the concrete subclass they implement
* (the xsi:type business). XXX Here, this subclass is set to
* ContinuousCells. Hopefully we come up with a better way to do this.
*
* @author rvosa
*/
public ContinuousMatrix createContinuousMatrix(OTUs otus) {
ContinuousMatrixImpl continuousMatrix = new ContinuousMatrixImpl(
getDocument());
mMatrixList.add(continuousMatrix);
getElement().appendChild(continuousMatrix.getElement());
continuousMatrix.setOTUs(otus);
continuousMatrix.getElement().setAttributeNS(XSI_URI, XSI_TYPE, NEX_PRE + ":ContinuousCells");
return continuousMatrix;
}
/**
* @param otus
* @param type specifies the molecular sequence type (Dna,Rna,Protein) This
* method creates the characters element and appends it to the
* document root. Because NeXML requires that characters elements
* have an id reference attribute to specify the otus element it
* refers to, the equivalent OTUs object needs to be passed in
* here. In addition, characters elements need to specify the
* concrete subclass they implement (the xsi:type business).
* XXX Here, this subclass is set to a molecular sequence type as
* specified in param type. Hopefully we come up with a better
* way to do this.
*
* @author pmidford
*/
public MolecularMatrix createMolecularMatrix(OTUs otus, String type) {
MolecularMatrixImpl molecularMatrix = new MolecularMatrixImpl(getDocument(),type);
mMatrixList.add(molecularMatrix);
getElement().appendChild(molecularMatrix.getElement());
molecularMatrix.setOTUs(otus);
return molecularMatrix;
}
/*
* (non-Javadoc)
* @see org.nexml.model.Document#getXmlString()
*/
public String getXmlString() {
StringWriter stringWriter = new StringWriter();
try {
getDocument().normalizeDocument();
Source source = new DOMSource(getElement());
Result result = new StreamResult(stringWriter);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(
"{http://xml.apache.org/xslt}indent-amount", "2");
transformer.transform(source, result);
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
return stringWriter.getBuffer().toString();
}
/*
* (non-Javadoc)
* @see org.nexml.model.Document#getOTUsList()
*/
public List<OTUs> getOTUsList() {
return mOtusList;
}
protected OTUs getOTUsById(String id) {
if ( null == id ) {
return null;
}
for ( OTUs otus : getOTUsList() ) {
if ( ((OTUsImpl)otus).getId().equals(id) ) {
return otus;
}
}
return null;
}
/*
* (non-Javadoc)
* @see org.nexml.model.Document#getMatrices()
*/
public List<Matrix<?>> getMatrices() {
return mMatrixList;
}
/*
* (non-Javadoc)
* @see org.nexml.model.Document#getTreeBlockList()
*/
public List<TreeBlock> getTreeBlockList() {
return mTreeBlockList;
}
/*
* (non-Javadoc)
* @see org.nexml.model.impl.NexmlWritableImpl#getId()
*/
public String getId() {
return null;
}
/*
* (non-Javadoc)
* @see org.nexml.model.impl.NexmlWritableImpl#setLabel(java.lang.String)
*/
public void setLabel(String label) {
}
/*
* (non-Javadoc)
* @see org.nexml.model.impl.NexmlWritableImpl#getLabel()
*/
public String getLabel() {
return null;
}
/*
* (non-Javadoc)
* @see org.nexml.model.Document#getMatrices(org.nexml.model.OTUs)
*/
@Override
public List<Matrix<?>> getMatrices(OTUs otus) {
if ( null == otus ) {
return null;
}
List<Matrix<?>> matricesForOTUs = new ArrayList<Matrix<?>>();
for ( Matrix<?> matrix : getMatrices()) {
if ( otus.equals(matrix.getOTUs()) ) {
matricesForOTUs.add(matrix);
}
}
return matricesForOTUs;
}
/*
* (non-Javadoc)
* @see org.nexml.model.Document#getTreeBlockList(org.nexml.model.OTUs)
*/
@Override
public List<TreeBlock> getTreeBlockList(OTUs otus) {
if ( null == otus ) {
return null;
}
List<TreeBlock> treeBlocksForOTUs = new ArrayList<TreeBlock>();
for ( TreeBlock treeBlock : getTreeBlockList() ) {
if ( otus.equals(treeBlock.getOTUs()) ) {
treeBlocksForOTUs.add(treeBlock);
}
}
return treeBlocksForOTUs;
}
}