package org.nexml.model.impl;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.nexml.model.Character;
import org.nexml.model.CharacterState;
import org.nexml.model.CharacterStateSet;
import org.nexml.model.MatrixCell;
import org.nexml.model.MolecularMatrix;
import org.nexml.model.OTU;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
class MolecularMatrixImpl extends
MatrixImpl<CharacterState> implements MolecularMatrix {
private Set<CharacterStateSet> mCharacterStateSets = new HashSet<CharacterStateSet>();
private MolecularCharacterStateSetImpl mMolecularCharacterStates = null;
public static final String DNA = "Dna";
public static final String RNA = "Rna";
public static final String Protein = "Protein";
/**
* 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
*/
protected MolecularMatrixImpl(Document document) {
super(document);
}
/**
* Protected constructors are intended for recursive parsing, i.e.
* starting from the root element (which maps onto DocumentImpl) we
* traverse the element tree such that for every child element that maps
* onto an Impl class the containing class calls that child's protected
* constructor, passes in the element of the child. From there the
* child takes over, populates itself and calls the protected
* constructors of its children. These should probably be protected
* because there is all sorts of opportunity for outsiders to call
* these in the wrong context, passing in the wrong elements etc.
* @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 equivalent NeXML element (e.g. for OTUsImpl, it's
* the <otus/> element)
* @author rvosa
*/
protected MolecularMatrixImpl(Document document, Element element, OTUsImpl otus, String typeName) {
super(document, element);
for ( Element stateSetElement : getChildrenByTagName( getFormatElement(), CharacterStateSetImpl.getTagNameClass() ) ) {
mMolecularCharacterStates = createCharacterStateSet(stateSetElement,typeName); // make specific types
}
for ( Element characterElement : getChildrenByTagName( getFormatElement(), CharacterImpl.getTagNameClass() ) ) {
createCharacter(characterElement);
}
for ( Element row : getChildrenByTagName( getMatrixElement(), "row") ) {
OTU otu = otus.getThingById(row.getAttribute("otu"));
for (Element cellElement : getChildrenByTagName(row, MatrixCellImpl.getTagNameClass() ) ) {
Character character = getThingById(cellElement.getAttribute("char"));
String stateId = cellElement.getAttribute("state");
CharacterState state = character.getCharacterStateSet().lookupCharacterStateById(stateId);
MatrixCell<CharacterState> matrixCell = createMatrixCell(otu, character, cellElement);
matrixCell.setValue(state);
}
for ( Element seqElement : getChildrenByTagName(row, "seq")) {
String seq = seqElement.getTextContent();
String[] states = element.getAttribute(XSI_TYPE).indexOf("Standard") > 0
? seq.split("\\s+") : seq.split("\\s*");
int k = 0;
STATE: for ( int j = 0; j < states.length; j++ ) {
if ( states[j].length() == 0 ) {
continue STATE;
}
Character character = getCharacterByIndex(k);
CharacterState state = character.getCharacterStateSet().lookupCharacterStateBySymbol(states[j]);
getCell(otu, character).setValue(state);
k++;
}
row.removeChild(seqElement);
}
}
setType(typeName);
setOTUs(otus);
}
protected MolecularMatrixImpl(Document document, String type) {
super(document,type);
this.createCharacterStateSet();
}
protected MolecularCharacterStateSetImpl createCharacterStateSet(Element statesElement,String typeName) {
MolecularCharacterStateSetImpl charStateSet = new MolecularCharacterStateSetImpl(getDocument(),statesElement);
for ( Element stateElement : getChildrenByTagName(statesElement,"state") ) {
CharacterState characterState = charStateSet.createCharacterState(stateElement);
charStateSet.addThing(characterState);
}
for ( Element stateElement : getChildrenByTagName(statesElement,"uncertain_state_set") ) {
CharacterState characterState = charStateSet.createUncertainCharacterState(stateElement);
charStateSet.addThing(characterState);
}
for ( Element stateElement : getChildrenByTagName(statesElement,"polymorphic_state_set") ) {
CharacterState characterState = charStateSet.createPolymorphicCharacterState(stateElement);
charStateSet.addThing(characterState);
}
mCharacterStateSets.add(charStateSet); // XXX Make this into a setter?
return charStateSet;
}
/**
* This is equivalent to creating a <states> element, i.e.
* a container for state elements, polymorphic_state_set elements
* and uncertain_state_set elements. The states elements are children
* of the format element (to which the matrix holds a reference).
* If the format element object doesn't exist yet it's created here
* @author rvosa
*/
public CharacterStateSet createCharacterStateSet() {
if ( mCharacterStateSets.isEmpty() ) {
MolecularCharacterStateSetImpl characterStateSet = new MolecularCharacterStateSetImpl(getDocument(),getType());
mCharacterStateSets.add(characterStateSet);
getFormatElement().insertBefore( characterStateSet.getElement(), getFormatElement().getFirstChild() );
mMolecularCharacterStates = characterStateSet;
}
return mMolecularCharacterStates;
}
/*
* (non-Javadoc)
* @see org.nexml.model.MolecularMatrix#getCharacterStateSets()
*/
public Set<CharacterStateSet> getCharacterStateSets() {
return Collections.unmodifiableSet(mCharacterStateSets);
}
/**
* This method creates a char element, i.e. a column definition.
* Because NeXML requires for categorical matrices that these
* column definitions have an attribute to reference the
* applicable state set, the state set object needs to be passed
* in here, from which the attribute's value is set.
* @author rvosa
*/
public Character createCharacter(CharacterStateSet characterStateSet) {
CharacterImpl character = new CharacterImpl(getDocument());
addThing(character);
character.setCharacterStateSet(characterStateSet);
getFormatElement().appendChild(character.getElement());
return character;
}
protected Character createCharacter(Element element) {
CharacterImpl character = new CharacterImpl(getDocument(),element);
addThing(character);
String stateSetId = element.getAttribute("states");
CharacterStateSet stateSet = lookupCharacterStateSetById(stateSetId);
character.setCharacterStateSet(stateSet);
return character;
}
protected CharacterStateSet lookupCharacterStateSetById(String stateSetId) {
if ( null == stateSetId ) {
return null;
}
for ( CharacterStateSet stateSet : mCharacterStateSets ) {
if ( stateSetId.equals(stateSet.getId()) ) {
return stateSet;
}
}
return null;
}
private CharacterStateSet getMolecularCharacterStateSet(String datatype) {
if (mMolecularCharacterStates == null){
return createCharacterStateSet();
}
return mMolecularCharacterStates;
}
/*
* (non-Javadoc)
* @see org.nexml.model.MolecularMatrix#getDNACharacterStateSet()
*/
public CharacterStateSet getDNACharacterStateSet() {
return getMolecularCharacterStateSet(DNA);
}
/*
* (non-Javadoc)
* @see org.nexml.model.MolecularMatrix#getRNACharacterStateSet()
*/
public CharacterStateSet getRNACharacterStateSet() {
return getMolecularCharacterStateSet(RNA);
}
/*
* (non-Javadoc)
* @see org.nexml.model.MolecularMatrix#getProteinCharacterStateSet()
*/
public CharacterStateSet getProteinCharacterStateSet(){
return getMolecularCharacterStateSet(Protein);
}
public CharacterState parseSymbol(String symbol) {
CharacterState state = null;
FIND_IN_SETS: for ( CharacterStateSet stateSet : getCharacterStateSets() ) {
state = stateSet.lookupCharacterStateBySymbol(symbol);
if ( null != state ) {
return state;
}
break FIND_IN_SETS;
}
state = mMolecularCharacterStates.lookupCharacterStateBySymbol(symbol);
return state;
}
public CharacterState parseSymbol(String symbol, String subDataType) {
if ( subDataType.equals(MolecularMatrix.DNA) ) {
if ( null == mMolecularCharacterStates ) {
mMolecularCharacterStates = (MolecularCharacterStateSetImpl) getDNACharacterStateSet();
}
return mMolecularCharacterStates.lookupCharacterStateBySymbol(symbol);
}
else if ( subDataType.equals(MolecularMatrix.RNA) ) {
if ( null == mMolecularCharacterStates ) {
mMolecularCharacterStates = (MolecularCharacterStateSetImpl) getRNACharacterStateSet();
}
return mMolecularCharacterStates.lookupCharacterStateBySymbol(symbol);
}
else if ( subDataType.equals(MolecularMatrix.Protein) ) {
if ( null == mMolecularCharacterStates ) {
mMolecularCharacterStates = (MolecularCharacterStateSetImpl) getProteinCharacterStateSet();
}
return mMolecularCharacterStates.lookupCharacterStateBySymbol(symbol);
}
// TODO Auto-generated method stub
return null;
}
@Override
public CharacterStateSet getCharacterStateSet() {
if ( getType().equals(DNA) ) {
return getDNACharacterStateSet();
}
else if ( getType().equals(RNA) ) {
return getRNACharacterStateSet();
}
else if ( getType().equals(Protein) ) {
return getProteinCharacterStateSet();
}
else {
throw new Error("No default state set for type "+getType());
}
}
}