diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62efe62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +.classpath +.project +.settings +bin \ No newline at end of file diff --git a/src/mesquite/nexml/InterpretNEXML/NexmlReaders/NexmlCharactersBlockReader.java b/src/mesquite/nexml/InterpretNEXML/NexmlReaders/NexmlCharactersBlockReader.java index 16e1b68..e999ae0 100644 --- a/src/mesquite/nexml/InterpretNEXML/NexmlReaders/NexmlCharactersBlockReader.java +++ b/src/mesquite/nexml/InterpretNEXML/NexmlReaders/NexmlCharactersBlockReader.java @@ -23,12 +23,14 @@ import org.nexml.model.Annotatable; import org.nexml.model.CategoricalMatrix; import org.nexml.model.Character; +import org.nexml.model.CompoundCharacterState; import org.nexml.model.ContinuousMatrix; import org.nexml.model.Matrix; import org.nexml.model.MatrixCell; import org.nexml.model.MolecularMatrix; import org.nexml.model.OTU; import org.nexml.model.OTUs; +import org.nexml.model.UncertainCharacterState; /** * @author rvosa @@ -70,7 +72,7 @@ else if ( xmlMatrix instanceof MolecularMatrix ) { return null; } } - + /** * * @param mesDataType @@ -90,21 +92,48 @@ private FileElement readMatrix(String mesDataType,Matrix xmlMatrix,MesquiteFi for ( Character xmlCharacter : xmlCharacterList ) { CharacterState mesCS = null; MatrixCell xmlCell = xmlMatrix.getCell(xmlOTU, xmlCharacter); - if ( mesMatrix instanceof ContinuousData ) { - Double xmlDouble = (Double)xmlCell.getValue(); - if ( xmlDouble != null ) { - mesCS = new ContinuousState(xmlDouble); - ((ContinuousState)mesCS).setNumItems(1); // XXX for multidimensional matrices - } - } - else { - org.nexml.model.CharacterState xmlState = (org.nexml.model.CharacterState)xmlCell.getValue(); - if ( xmlState != null ) { - mesCS = new CategoricalState(); - String xmlSymbol = xmlState.getSymbol().toString(); - mesCS.setValue(xmlSymbol, mesMatrix); - } - } + if ( mesMatrix instanceof ContinuousData ) { + Double xmlDouble = (Double)xmlCell.getValue(); + if ( xmlDouble != null ) { + mesCS = new ContinuousState(xmlDouble); + ((ContinuousState)mesCS).setNumItems(1); // XXX for multidimensional matrices + } + } + else { + if (xmlMatrix instanceof CategoricalMatrix) { + for (org.nexml.model.CharacterState state : xmlCharacter.getCharacterStateSet().getCharacterStates()) { + if (!(state instanceof CompoundCharacterState)) { + String label = state.getLabel(); + if ((null != label) && (!label.equals("")) && (mesMatrix instanceof CategoricalData)) { + int stateIndex = Integer.parseInt(state.getSymbol().toString()); + ((CategoricalData)mesMatrix).setStateName(mesCharacter, stateIndex, label); + } + } + } + } + org.nexml.model.CharacterState xmlState = (org.nexml.model.CharacterState)xmlCell.getValue(); + if ( xmlState != null ) { + String xmlSymbol = xmlState.getSymbol().toString(); + if (xmlMatrix instanceof CategoricalMatrix) { + long stateValue = CategoricalState.emptySet(); + if (xmlState instanceof CompoundCharacterState) { + for (org.nexml.model.CharacterState state : ((CompoundCharacterState)xmlState).getStates()) { + int memberSymbol = Integer.parseInt(state.getSymbol().toString()); + stateValue = CategoricalState.addToSet(stateValue, memberSymbol); + } + if (xmlState instanceof UncertainCharacterState) { + stateValue = CategoricalState.setUncertainty(stateValue, true); + } + } else { + stateValue = CategoricalState.makeSet(Integer.parseInt(xmlSymbol)); + } + mesCS = new CategoricalState(stateValue); + } else { + mesCS = new CategoricalState(); + mesCS.setValue(xmlSymbol, mesMatrix); + } + } + } if ( mesCS != null ) { mesMatrix.setState(mesCharacter, mesTaxon, mesCS); //can add in character state stuff here diff --git a/src/mesquite/nexml/InterpretNEXML/NexmlWriters/NexmlCharactersBlockWriter.java b/src/mesquite/nexml/InterpretNEXML/NexmlWriters/NexmlCharactersBlockWriter.java index ab27656..033afe1 100644 --- a/src/mesquite/nexml/InterpretNEXML/NexmlWriters/NexmlCharactersBlockWriter.java +++ b/src/mesquite/nexml/InterpretNEXML/NexmlWriters/NexmlCharactersBlockWriter.java @@ -2,10 +2,13 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import mesquite.categ.lib.CategoricalData; +import mesquite.categ.lib.CategoricalState; import mesquite.categ.lib.DNAData; import mesquite.categ.lib.ProteinData; import mesquite.categ.lib.RNAData; @@ -22,6 +25,7 @@ import org.nexml.model.CategoricalMatrix; import org.nexml.model.Character; import org.nexml.model.CharacterStateSet; +import org.nexml.model.CompoundCharacterState; import org.nexml.model.Document; import org.nexml.model.Matrix; import org.nexml.model.MatrixCell; @@ -29,16 +33,23 @@ import org.nexml.model.NexmlWritable; import org.nexml.model.OTU; import org.nexml.model.OTUs; +import org.nexml.model.PolymorphicCharacterState; +import org.nexml.model.UncertainCharacterState; public class NexmlCharactersBlockWriter extends NexmlBlockWriter { - + + /** + * Generate symbols for uncertainties and polymorphisms that don't conflict with existing state symbols. + */ + private int nextMultipleStateSymbol = CategoricalState.getMaxPossibleStateStatic() + 1; + @SuppressWarnings("serial") private static final Map xmlMolecularDataTypeFor = new HashMap() {{ put(DNAData.DATATYPENAME, MolecularMatrix.DNA); put(RNAData.DATATYPENAME, MolecularMatrix.RNA); put(ProteinData.DATATYPENAME, MolecularMatrix.Protein); }}; - + /** * * @param employerEmployee @@ -57,26 +68,23 @@ protected Annotatable writeBlock(Document xmlProject, FileElement mesBlock) { Taxa mesTaxa = mesData.getTaxa(); OTUs xmlTaxa = findEquivalentTaxa(mesTaxa,xmlProject); org.nexml.model.Matrix xmlMatrix = null; - CharacterStateSet xmlCharacterStateSet = null; String mesDataType = mesData.getDataTypeName(); if ( xmlMolecularDataTypeFor.containsKey(mesDataType) ) { xmlMatrix = xmlProject.createMolecularMatrix(xmlTaxa,xmlMolecularDataTypeFor.get(mesDataType)); - xmlCharacterStateSet = ((MolecularMatrix)xmlMatrix).getCharacterStateSet(); } else if ( mesDataType.equalsIgnoreCase(CategoricalData.DATATYPENAME) ) { xmlMatrix = xmlProject.createCategoricalMatrix(xmlTaxa); - xmlCharacterStateSet = ((CategoricalMatrix)xmlMatrix).createCharacterStateSet(); } else if ( mesDataType.equalsIgnoreCase(ContinuousData.DATATYPENAME) ) { xmlMatrix = xmlProject.createContinuousMatrix(xmlTaxa); } else { MesquiteMessage.warnProgrammer("Can't write data type "+mesDataType); - } - writeCharacterStates(mesData, xmlMatrix, xmlCharacterStateSet); + } + writeCharacterStates(mesData, xmlMatrix); return xmlMatrix; } - + /** * * @param mesData @@ -84,39 +92,77 @@ else if ( mesDataType.equalsIgnoreCase(ContinuousData.DATATYPENAME) ) { * @param xmlCharacterStateSet */ @SuppressWarnings("unchecked") - private void writeCharacterStates(CharacterData mesData, org.nexml.model.Matrix xmlMatrix, CharacterStateSet xmlCharacterStateSet) { + private void writeCharacterStates(CharacterData mesData, org.nexml.model.Matrix xmlMatrix) { String mesDataType = mesData.getDataTypeName(); int mesNchar = mesData.getNumChars(); List xmlCharacters = new ArrayList(mesNchar); - for ( int j = 0; j < mesNchar; j++ ) { + for ( int characterIndex = 0; characterIndex < mesNchar; characterIndex++ ) { + CharacterStateSet xmlCharacterStateSet = null; + if ( xmlMolecularDataTypeFor.containsKey(mesDataType) ) { + xmlCharacterStateSet = ((MolecularMatrix)xmlMatrix).getCharacterStateSet(); + } + else if ( mesDataType.equalsIgnoreCase(CategoricalData.DATATYPENAME) ) { + xmlCharacterStateSet = ((CategoricalMatrix)xmlMatrix).createCharacterStateSet(); + } Character xmlChar = xmlMatrix.createCharacter(xmlCharacterStateSet); - String mesCharacterName = mesData.getCharacterName(j); + String mesCharacterName = mesData.getCharacterName(characterIndex); if ( null != mesCharacterName && ! mesCharacterName.equals("") ) { xmlChar.setLabel(mesCharacterName); } + if ( mesDataType.equalsIgnoreCase(CategoricalData.DATATYPENAME) ) { + CategoricalData data = ((CategoricalData)mesData); + int maxStateIndex = data.maxStateWithName(characterIndex); + for (int stateIndex = 0; stateIndex <= maxStateIndex; stateIndex++) { + String symbol = String.valueOf(data.getSymbol(stateIndex)); + org.nexml.model.CharacterState state = xmlChar.getCharacterStateSet().createCharacterState(symbol); + state.setSymbol(symbol); + if (data.hasStateName(characterIndex, stateIndex)) { + String stateLabel = data.getStateName(characterIndex, stateIndex); + state.setLabel(stateLabel); + } + } + } xmlCharacters.add(xmlChar); } - for ( int j = 0; j < mesData.getNumTaxa(); j++ ) { - CharacterState[] mesChars = mesData.getCharacterStateArray(j, 0, mesNchar); - Taxon mesTaxon = mesData.getTaxa().getTaxon(j); + for (int taxonIndex = 0; taxonIndex < mesData.getNumTaxa(); taxonIndex++) { + CharacterState[] mesCharStates = mesData.getCharacterStateArray(taxonIndex, 0, mesNchar); + Taxon mesTaxon = mesData.getTaxa().getTaxon(taxonIndex); OTU xmlTaxon = findEquivalentTaxon(mesTaxon,xmlMatrix.getOTUs()); - for ( int k = 0; k < mesNchar; k++ ) { - Character xmlChar = xmlCharacters.get(k); - String mesCharString = mesChars[k].toDisplayString(); - if ( mesCharString != null && !mesCharString.equals("-") ) { - if ( mesDataType.equalsIgnoreCase(ContinuousData.DATATYPENAME) ) { - MatrixCell xmlCell = (MatrixCell) xmlMatrix.getCell(xmlTaxon,xmlChar); - xmlCell.setValue((Double)xmlMatrix.parseSymbol(mesCharString)); + for ( int characterIndex = 0; characterIndex < mesNchar; characterIndex++ ) { + Character xmlChar = xmlCharacters.get(characterIndex); + CharacterState mesState = mesCharStates[characterIndex]; + if (mesDataType.equalsIgnoreCase(CategoricalData.DATATYPENAME)) { + CharacterStateSet xmlStateSet = xmlChar.getCharacterStateSet(); + CategoricalData categoricalData = (CategoricalData)mesData; + long stateAssignment = categoricalData.getState(characterIndex, taxonIndex); + org.nexml.model.CharacterState xmlCharacterState = null; + if (CategoricalState.hasMultipleStates(stateAssignment)) { + Set symbols = new HashSet(); + for (int mesStateCode : CategoricalState.expand(stateAssignment)) { + symbols.add(String.valueOf(categoricalData.getSymbol(mesStateCode))); + } + if (CategoricalState.isUncertain(stateAssignment)) { + xmlCharacterState = findOrCreateUncertainStateSet(xmlStateSet, symbols); + } else { //polymorphic + xmlCharacterState = findOrCreatePolymorphicStateSet(xmlStateSet, symbols); + } + } else { // single state + if ((!CategoricalState.isUnassigned(stateAssignment)) && (!CategoricalState.isInapplicable(stateAssignment))) { + String symbol = String.valueOf(categoricalData.getSymbol(CategoricalState.getOnlyElement(stateAssignment))); + xmlCharacterState = xmlStateSet.lookupCharacterStateBySymbol(symbol); + } } - else if ( mesDataType.equalsIgnoreCase(CategoricalData.DATATYPENAME) ) { - MatrixCell xmlCell = (MatrixCell) xmlMatrix.getCell(xmlTaxon,xmlChar); - xmlCell.setValue((org.nexml.model.CharacterState)xmlMatrix.parseSymbol(mesCharString)); + if (xmlCharacterState != null) { + MatrixCell xmlCell = (MatrixCell) xmlMatrix.getCell(xmlTaxon, xmlChar); + xmlCell.setValue(xmlCharacterState); } - else if ( xmlMolecularDataTypeFor.containsKey(mesDataType) ) { - MatrixCell xmlCell = (MatrixCell) xmlMatrix.getCell(xmlTaxon,xmlChar); - xmlCell.setValue((org.nexml.model.CharacterState)((MolecularMatrix)xmlMatrix).parseSymbol(mesCharString,xmlMolecularDataTypeFor.get(mesDataType))); - } - } + } else if (mesDataType.equalsIgnoreCase(ContinuousData.DATATYPENAME)) { + MatrixCell xmlCell = (MatrixCell) xmlMatrix.getCell(xmlTaxon,xmlChar); + xmlCell.setValue((Double)xmlMatrix.parseSymbol(mesState.toDisplayString(), xmlChar)); + } else if ( xmlMolecularDataTypeFor.containsKey(mesDataType) ) { + MatrixCell xmlCell = (MatrixCell) xmlMatrix.getCell(xmlTaxon,xmlChar); + xmlCell.setValue((org.nexml.model.CharacterState)((MolecularMatrix)xmlMatrix).parseSymbol(mesState.toDisplayString(), xmlMolecularDataTypeFor.get(mesDataType))); + } } } } @@ -131,4 +177,51 @@ protected Annotatable getThingInXmlBlock(NexmlWritable xmlBlock, int index) { return xmlMatrix.getCharacters().get(index); } + private UncertainCharacterState findOrCreateUncertainStateSet(CharacterStateSet containingStateSet, Set symbols) { + for (org.nexml.model.CharacterState state : containingStateSet.getCharacterStates()) { + if (state instanceof UncertainCharacterState) { + UncertainCharacterState uncertainState = (UncertainCharacterState)state; + if (containsMatchingStates(uncertainState, symbols)) { + return uncertainState; + } + } + } + Set memberStates = collectMatchingStates(containingStateSet, symbols); + return containingStateSet.createUncertainCharacterState(this.nextMultipleStateSymbol++, memberStates); + } + + private PolymorphicCharacterState findOrCreatePolymorphicStateSet(CharacterStateSet containingStateSet, Set symbols) { + for (org.nexml.model.CharacterState state : containingStateSet.getCharacterStates()) { + if (state instanceof PolymorphicCharacterState) { + PolymorphicCharacterState polymorphicState = (PolymorphicCharacterState)state; + if (containsMatchingStates(polymorphicState, symbols)) { + return polymorphicState; + } + } + } + Set memberStates = collectMatchingStates(containingStateSet, symbols); + return containingStateSet.createPolymorphicCharacterState(this.nextMultipleStateSymbol++, memberStates); + } + + private boolean containsMatchingStates(CompoundCharacterState state, Set symbols) { + Set containedSymbols = new HashSet(); + for (org.nexml.model.CharacterState containedState : state.getStates()) { + containedSymbols.add(containedState.getSymbol().toString()); + } + return containedSymbols.equals(symbols); + } + + private Set collectMatchingStates(CharacterStateSet containingStateSet, Set symbols) { + Set memberStates = new HashSet(); + for (String symbol : symbols) { + org.nexml.model.CharacterState member = containingStateSet.lookupCharacterStateBySymbol(symbol); + if ( null != member ) { + memberStates.add(member); + } else { + memberStates.add(containingStateSet.createCharacterState(symbol)); + } + } + return memberStates; + } + } diff --git a/src/org/nexml/model/Matrix.java b/src/org/nexml/model/Matrix.java index 169b039..d9576f0 100644 --- a/src/org/nexml/model/Matrix.java +++ b/src/org/nexml/model/Matrix.java @@ -69,7 +69,7 @@ public interface Matrix extends OTUsLinkable, Annotatable, Segmented value) { mValue = value; getElement().setAttribute(XSI_TYPE,ResourceMeta); for ( Annotation annotation : value ) { - getElement().appendChild(((AnnotationImpl)annotation).getElement()); + Node node = getElement().getOwnerDocument().adoptNode(((AnnotationImpl)annotation).getElement()); + getElement().appendChild(node); } } diff --git a/src/org/nexml/model/impl/CategoricalMatrixImpl.java b/src/org/nexml/model/impl/CategoricalMatrixImpl.java index 0742d72..5bc9996 100644 --- a/src/org/nexml/model/impl/CategoricalMatrixImpl.java +++ b/src/org/nexml/model/impl/CategoricalMatrixImpl.java @@ -14,56 +14,56 @@ import org.w3c.dom.Element; class CategoricalMatrixImpl extends - MatrixImpl implements CategoricalMatrix { - +MatrixImpl implements CategoricalMatrix { + private Set mCharacterStateSets = new HashSet(); private MolecularCharacterStateSetImpl mMolecularCharacterStates = null; - - /** - * 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 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 CategoricalMatrixImpl(Document document) { super(document,"Standard"); } - - /** - * 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 element) - * @author rvosa - */ + + /** + * 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 element) + * @author rvosa + */ protected CategoricalMatrixImpl(Document document, Element element, OTUsImpl otus) { super(document, element); for ( Element stateSetElement : getChildrenByTagName( getFormatElement(), CharacterStateSetImpl.getTagNameClass() ) ) { createCharacterStateSet(stateSetElement); } - + for ( Element characterElement : getChildrenByTagName( getFormatElement(), CharacterImpl.getTagNameClass() ) ) { createCharacter(characterElement); } - + for ( Element row : getChildrenByTagName( getMatrixElement(), "row") ) { OTU otu = otus.getThingById(row.getAttribute("otu")); - MatrixRowImpl matrixRow = new MatrixRowImpl(getDocument(),row); + MatrixRowImpl matrixRow = new MatrixRowImpl(getDocument(),row, this, false); matrixRow.setOTU(otu); mMatrixRows.put(otu, matrixRow); } @@ -87,7 +87,7 @@ protected CharacterStateSet createCharacterStateSet(Element statesElement) { mCharacterStateSets.add(charStateSet); // XXX Make this into a setter? return charStateSet; } - + /** * This is equivalent to creating a element, i.e. * a container for state elements, polymorphic_state_set elements @@ -96,6 +96,7 @@ protected CharacterStateSet createCharacterStateSet(Element statesElement) { * If the format element object doesn't exist yet it's created here * @author rvosa */ + @Override public CharacterStateSet createCharacterStateSet() { CharacterStateSetImpl characterStateSet = new CharacterStateSetImpl(getDocument()); List currentCharElements = getChildrenByTagName(getFormatElement(), "char"); @@ -119,10 +120,11 @@ else if ( ! currentSetElements.isEmpty() ) { * (non-Javadoc) * @see org.nexml.model.CategoricalMatrix#getCharacterStateSets() */ + @Override public Set getCharacterStateSets() { return Collections.unmodifiableSet(mCharacterStateSets); } - + /** * This method creates a char element, i.e. a column definition. * Because NeXML requires for categorical matrices that these @@ -131,6 +133,7 @@ public Set getCharacterStateSets() { * in here, from which the attribute's value is set. * @author rvosa */ + @Override public Character createCharacter(CharacterStateSet characterStateSet) { CharacterImpl character = new CharacterImpl(getDocument()); addThing(character); @@ -138,7 +141,7 @@ public Character createCharacter(CharacterStateSet characterStateSet) { attachFundamentalDataElement(getFormatElement(), character.getElement()); return character; } - + protected Character createCharacter(Element element) { CharacterImpl character = new CharacterImpl(getDocument(),element); addThing(character); @@ -147,7 +150,7 @@ protected Character createCharacter(Element element) { character.setCharacterStateSet(stateSet); return character; } - + protected CharacterStateSet lookupCharacterStateSetById(String stateSetId) { if ( null == stateSetId ) { return null; @@ -161,63 +164,62 @@ protected CharacterStateSet lookupCharacterStateSetById(String stateSetId) { } public CharacterStateSet getDNACharacterStateSet() { - if (mMolecularCharacterStates == null){ - mMolecularCharacterStates = new MolecularCharacterStateSetImpl(getDocument()); - } - CharacterStateSet result = mMolecularCharacterStates.getDNAStateSet(); - CharacterStateSetImpl characterStateSet = (CharacterStateSetImpl)result; - if (mCharacterStateSets.add(characterStateSet)){ - if ( null == getFormatElement() ) { - setFormatElement( getDocument().createElementNS(DEFAULT_NAMESPACE,"format") ); - getElement().insertBefore( getFormatElement(), getElement().getFirstChild() ); - } - getFormatElement().insertBefore( characterStateSet.getElement(), getFormatElement().getFirstChild() ); - } - return result; + if (mMolecularCharacterStates == null){ + mMolecularCharacterStates = new MolecularCharacterStateSetImpl(getDocument()); + } + CharacterStateSet result = mMolecularCharacterStates.getDNAStateSet(); + CharacterStateSetImpl characterStateSet = (CharacterStateSetImpl)result; + if (mCharacterStateSets.add(characterStateSet)){ + if ( null == getFormatElement() ) { + setFormatElement( getDocument().createElementNS(DEFAULT_NAMESPACE,"format") ); + getElement().insertBefore( getFormatElement(), getElement().getFirstChild() ); + } + getFormatElement().insertBefore( characterStateSet.getElement(), getFormatElement().getFirstChild() ); + } + return result; } public CharacterStateSet getRNACharacterStateSet() { - if (mMolecularCharacterStates == null){ - mMolecularCharacterStates = new MolecularCharacterStateSetImpl(getDocument()); - } - CharacterStateSet result = mMolecularCharacterStates.getRNAStateSet(); - CharacterStateSetImpl characterStateSet = (CharacterStateSetImpl)result; - if (mCharacterStateSets.add(characterStateSet)){ - if ( null == getFormatElement() ) { - setFormatElement( getDocument().createElementNS(DEFAULT_NAMESPACE,"format") ); - getElement().insertBefore( getFormatElement(), getElement().getFirstChild() ); - } - getFormatElement().insertBefore( characterStateSet.getElement(), getFormatElement().getFirstChild() ); - } - return result; - } - - public CharacterStateSet getProteinCharacterStateSet(){ - if (mMolecularCharacterStates == null){ - mMolecularCharacterStates = new MolecularCharacterStateSetImpl(getDocument()); - } - CharacterStateSet result = mMolecularCharacterStates.getProteinStateSet(); - CharacterStateSetImpl characterStateSet = (CharacterStateSetImpl)result; - if (mCharacterStateSets.add(characterStateSet)){ - if ( null == getFormatElement() ) { - setFormatElement( getDocument().createElementNS(DEFAULT_NAMESPACE,"format") ); - getElement().insertBefore( getFormatElement(), getElement().getFirstChild() ); - } - getFormatElement().insertBefore( characterStateSet.getElement(), getFormatElement().getFirstChild() ); - } - return result; - } - - public CharacterState parseSymbol(String symbol) { - CharacterStateSet lastSet = null; - for ( CharacterStateSet stateSet : getCharacterStateSets() ) { - CharacterState state = stateSet.lookupCharacterStateBySymbol(symbol); - if ( null != state ) { - return state; + if (mMolecularCharacterStates == null){ + mMolecularCharacterStates = new MolecularCharacterStateSetImpl(getDocument()); + } + CharacterStateSet result = mMolecularCharacterStates.getRNAStateSet(); + CharacterStateSetImpl characterStateSet = (CharacterStateSetImpl)result; + if (mCharacterStateSets.add(characterStateSet)){ + if ( null == getFormatElement() ) { + setFormatElement( getDocument().createElementNS(DEFAULT_NAMESPACE,"format") ); + getElement().insertBefore( getFormatElement(), getElement().getFirstChild() ); } - lastSet = stateSet; + getFormatElement().insertBefore( characterStateSet.getElement(), getFormatElement().getFirstChild() ); + } + return result; + } + + public CharacterStateSet getProteinCharacterStateSet(){ + if (mMolecularCharacterStates == null){ + mMolecularCharacterStates = new MolecularCharacterStateSetImpl(getDocument()); + } + CharacterStateSet result = mMolecularCharacterStates.getProteinStateSet(); + CharacterStateSetImpl characterStateSet = (CharacterStateSetImpl)result; + if (mCharacterStateSets.add(characterStateSet)){ + if ( null == getFormatElement() ) { + setFormatElement( getDocument().createElementNS(DEFAULT_NAMESPACE,"format") ); + getElement().insertBefore( getFormatElement(), getElement().getFirstChild() ); + } + getFormatElement().insertBefore( characterStateSet.getElement(), getFormatElement().getFirstChild() ); + } + return result; + } + + @Override + public CharacterState parseSymbol(String symbol, Character character) { + CharacterStateSet stateSet = character.getCharacterStateSet(); + CharacterState state = stateSet.lookupCharacterStateBySymbol(symbol); + if ( null != state ) { + return state; + } else { + return stateSet.createCharacterState(symbol); } - return lastSet.createCharacterState(symbol); } @Override @@ -225,4 +227,4 @@ String getSplitString() { return "\\s+"; } - } +} diff --git a/src/org/nexml/model/impl/CharacterStateSetImpl.java b/src/org/nexml/model/impl/CharacterStateSetImpl.java index 94d06df..2a31ef8 100644 --- a/src/org/nexml/model/impl/CharacterStateSetImpl.java +++ b/src/org/nexml/model/impl/CharacterStateSetImpl.java @@ -137,10 +137,9 @@ protected CharacterState createCharacterState(Element stateElement) { * perhaps that needs to change. * @author rvosa */ - public PolymorphicCharacterState createPolymorphicCharacterState( - Object symbol, - Set members) { + public PolymorphicCharacterState createPolymorphicCharacterState(Object symbol,Set members) { PolymorphicCharacterStateImpl polymorphicCharacterStateImpl = new PolymorphicCharacterStateImpl(getDocument()); + getElement().appendChild(polymorphicCharacterStateImpl.getElement()); polymorphicCharacterStateImpl.setSymbol(symbol); polymorphicCharacterStateImpl.setStates(members); getCharacterStates().add(polymorphicCharacterStateImpl); @@ -173,9 +172,7 @@ private void populateCompoundCharacterState(CompoundCharacterState state, Elemen /** * XXX see discussion for createPolymorphicCharacterState() */ - public UncertainCharacterState createUncertainCharacterState( - Object symbol, - Set members) { + public UncertainCharacterState createUncertainCharacterState(Object symbol, Set members) { UncertainCharacterStateImpl uncertainCharacterStateImpl = new UncertainCharacterStateImpl(getDocument()); getElement().appendChild(uncertainCharacterStateImpl.getElement()); uncertainCharacterStateImpl.setSymbol(symbol); diff --git a/src/org/nexml/model/impl/ContinuousMatrixImpl.java b/src/org/nexml/model/impl/ContinuousMatrixImpl.java index 67d253a..7ad6086 100644 --- a/src/org/nexml/model/impl/ContinuousMatrixImpl.java +++ b/src/org/nexml/model/impl/ContinuousMatrixImpl.java @@ -63,7 +63,7 @@ private void processMatrix(Element matrix,OTUsImpl otus,Map ch setMatrixElement(matrix); for ( Element rowElement : getChildrenByTagName(matrix, "row") ) { OTU otu = otus.getThingById(rowElement.getAttribute("otu")); - MatrixRow matrixRow = new MatrixRowImpl(getDocument(),rowElement); + MatrixRow matrixRow = new MatrixRowImpl(getDocument(),rowElement, this, true); matrixRow.setOTU(otu); mMatrixRows.put(otu, matrixRow); } @@ -99,7 +99,8 @@ public Character createCharacter(CharacterStateSet stateSet) { return createCharacter(); } - public Double parseSymbol(String symbol) { + @Override + public Double parseSymbol(String symbol, Character character) { return Double.parseDouble(symbol); } diff --git a/src/org/nexml/model/impl/MatrixImpl.java b/src/org/nexml/model/impl/MatrixImpl.java index a56acf7..2e0a2a1 100644 --- a/src/org/nexml/model/impl/MatrixImpl.java +++ b/src/org/nexml/model/impl/MatrixImpl.java @@ -62,14 +62,7 @@ protected MatrixImpl(Document document,String type) { * @author rvosa */ protected MatrixImpl(Document document,Element element) { - super(document,element); - for (Element rowElement : getChildrenByTagName(element, "row")) { - String otuId = rowElement.getAttribute("otu"); - OTU otu = ((OTUsImpl)getOTUs()).getThingById(otuId); - MatrixRow row = new MatrixRowImpl(document, rowElement); - row.setOTU(otu); - mMatrixRows.put(otu, row); - } + super(document,element); } /* diff --git a/src/org/nexml/model/impl/MatrixRowImpl.java b/src/org/nexml/model/impl/MatrixRowImpl.java index 49c07f2..0847a8d 100644 --- a/src/org/nexml/model/impl/MatrixRowImpl.java +++ b/src/org/nexml/model/impl/MatrixRowImpl.java @@ -6,6 +6,7 @@ import java.util.Map; import org.nexml.model.Character; +import org.nexml.model.CharacterState; import org.nexml.model.MatrixCell; import org.nexml.model.MatrixRow; import org.w3c.dom.Document; @@ -55,15 +56,27 @@ protected MatrixRowImpl(Document document,MatrixImpl matrix) { * the element) * @author rvosa */ - protected MatrixRowImpl(Document document, Element element) { + protected MatrixRowImpl(Document document, Element element, MatrixImpl matrix, boolean continuous) { super(document, element); + mMatrix = matrix; List seqElements = getChildrenByTagName(element, "seq"); if ( ! seqElements.isEmpty() ) { mSeqElement = seqElements.get(0); } List cellElements = getChildrenByTagName(element, "cell"); for ( Element cellElement : cellElements ) { + Character character = findCharacter(cellElement.getAttribute("char")); + Object value; + if (continuous) { + value = Double.parseDouble(cellElement.getAttribute("state")); + + } else { + value = findState(cellElement.getAttribute("state"), character); + } + mStateForCharacter.put(character, (T)value); MatrixCellImpl cell = new MatrixCellImpl(getDocument(),cellElement); + cell.setValue((T)value); + mCellForCharacter.put(character, cell); mMatrixCell.add(cell); } } @@ -195,5 +208,23 @@ public String getSeq() { private MatrixImpl getMatrix() { return mMatrix; } + + private Character findCharacter(String id) { + for (Character character : getMatrix().getCharacters()) { + if (character.getId().equals(id)) { + return character; + } + } + return null; + } + + private CharacterState findState(String id, Character character) { + for (CharacterState state : character.getCharacterStateSet().getCharacterStates()) { + if (state.getId().equals(id)) { + return state; + } + } + return null; + } } diff --git a/src/org/nexml/model/impl/MolecularMatrixImpl.java b/src/org/nexml/model/impl/MolecularMatrixImpl.java index 7869d9b..7168684 100644 --- a/src/org/nexml/model/impl/MolecularMatrixImpl.java +++ b/src/org/nexml/model/impl/MolecularMatrixImpl.java @@ -65,7 +65,7 @@ protected MolecularMatrixImpl(Document document, Element element, OTUsImpl otus, for ( Element row : getChildrenByTagName( getMatrixElement(), "row") ) { OTU otu = otus.getThingById(row.getAttribute("otu")); - MatrixRowImpl matrixRow = new MatrixRowImpl(getDocument(),row); + MatrixRowImpl matrixRow = new MatrixRowImpl(getDocument(),row, this, false); matrixRow.setOTU(otu); mMatrixRows.put(otu, matrixRow); } @@ -189,7 +189,8 @@ public CharacterStateSet getProteinCharacterStateSet(){ return getMolecularCharacterStateSet(Protein); } - public CharacterState parseSymbol(String symbol) { + @Override + public CharacterState parseSymbol(String symbol, Character character) { CharacterState state = null; FIND_IN_SETS: for ( CharacterStateSet stateSet : getCharacterStateSets() ) { state = stateSet.lookupCharacterStateBySymbol(symbol); diff --git a/test/org/nexml/model/TestCreateMolecular.java b/test/org/nexml/model/TestCreateMolecular.java index 13491db..32405f3 100644 --- a/test/org/nexml/model/TestCreateMolecular.java +++ b/test/org/nexml/model/TestCreateMolecular.java @@ -35,7 +35,7 @@ public void testCreateDNAMatrix () { for ( OTU otu : otus.getAllOTUs() ) { for ( Character character : characters ) { Long l = new Long(Math.round(Math.random()*(symbols.length-1))); - matrix.getCell(otu, character).setValue(matrix.parseSymbol(symbols[l.intValue()])); + matrix.getCell(otu, character).setValue(matrix.parseSymbol(symbols[l.intValue()], character)); } } @@ -72,7 +72,7 @@ public void testCreateRNAMatrix () { for ( OTU otu : otus.getAllOTUs() ) { for ( Character character : characters ) { Long l = new Long(Math.round(Math.random()*(symbols.length-1))); - matrix.getCell(otu, character).setValue(matrix.parseSymbol(symbols[l.intValue()])); + matrix.getCell(otu, character).setValue(matrix.parseSymbol(symbols[l.intValue()], character)); } } @@ -109,7 +109,7 @@ public void testCreateProteinMatrix () { for ( OTU otu : otus.getAllOTUs() ) { for ( Character character : characters ) { Long l = new Long(Math.round(Math.random()*(symbols.length-1))); - matrix.getCell(otu, character).setValue(matrix.parseSymbol(symbols[l.intValue()])); + matrix.getCell(otu, character).setValue(matrix.parseSymbol(symbols[l.intValue()], character)); } } @@ -145,7 +145,7 @@ public void testCreateThreeTypes () { for ( OTU otu : otus.getAllOTUs() ) { for ( Character character : dnaCharacters ) { Long l = new Long(Math.round(Math.random()*(dnaSymbols.length-1))); - dnaMatrix.getCell(otu, character).setValue(dnaMatrix.parseSymbol(dnaSymbols[l.intValue()])); + dnaMatrix.getCell(otu, character).setValue(dnaMatrix.parseSymbol(dnaSymbols[l.intValue()], character)); } } @@ -164,7 +164,7 @@ public void testCreateThreeTypes () { for ( OTU otu : otus.getAllOTUs() ) { for ( Character character : rnaCharacters ) { Long l = new Long(Math.round(Math.random()*(rnaSymbols.length-1))); - rnaMatrix.getCell(otu, character).setValue(rnaMatrix.parseSymbol(rnaSymbols[l.intValue()])); + rnaMatrix.getCell(otu, character).setValue(rnaMatrix.parseSymbol(rnaSymbols[l.intValue()], character)); } } @@ -183,7 +183,7 @@ public void testCreateThreeTypes () { for ( OTU otu : otus.getAllOTUs() ) { for ( Character character : proteinCharacters ) { Long l = new Long(Math.round(Math.random()*(proteinSymbols.length-1))); - proteinMatrix.getCell(otu, character).setValue(proteinMatrix.parseSymbol(proteinSymbols[l.intValue()])); + proteinMatrix.getCell(otu, character).setValue(proteinMatrix.parseSymbol(proteinSymbols[l.intValue()], character)); } } diff --git a/test/org/nexml/model/TestFileParse.java b/test/org/nexml/model/TestFileParse.java index f68652e..576c392 100644 --- a/test/org/nexml/model/TestFileParse.java +++ b/test/org/nexml/model/TestFileParse.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.IOException; + import javax.xml.parsers.ParserConfigurationException; import junit.framework.Assert; @@ -39,5 +40,18 @@ public void parseCharacters() { } System.out.println(doc.getXmlString()); } + + @Test + public void parsePhenoscapeMatrix() throws SAXException, IOException, ParserConfigurationException { + final File file = new File("test_files/Buckup_1998.xml"); + final Document doc = DocumentFactory.parse(file); + for (Matrix matrix : doc.getMatrices()) { + for (Character character : matrix.getCharacters()) { + for (OTU otu : matrix.getOTUs()) { + System.out.println(matrix.getRowObject(otu).getCell(character).getValue()); + } + } + } + } } diff --git a/test_files/Buckup_1998.xml b/test_files/Buckup_1998.xml new file mode 100644 index 0000000..6015755 --- /dev/null +++ b/test_files/Buckup_1998.xml @@ -0,0 +1,5668 @@ + + + Wasila Dahdul + PSPUB:0000013 + Evidence codes for matrix is IVS. Outgroup removed because paper does not provide taxa examined. + + + + 1 + Acestrorhynchus + + + 205830 + + + + 206997 + + + + 207388 + + + + 207768 + + + + 207850 + + + + + 3A + Alestes + + + 200349 + + + + 200100 + + + + + Boulengerella + + + 216327 + + + + 209065 + + + + + Brycon + + + 183916 + + + + 189940 + + + + + Bryconops + + + 215778 + + + + + Characidium + + + 205658 + + + + + 5C + Charax + + + 207857 + + + + 207313 + + + + + Chilodus + + + 205198 + + + + + Citharinus + + + 50443 + + + + + 3B + Crenuchus + + + 225688 + + + + 289577 + + + + + Ctenolucius + + + 146632 + + + + + 5B + Cynopotamus + + + 208075 + + + + + Distichodus + + + 77826 + + + + + Hemiodus + + + 205207 + + + + 204955 + + + + + Hepsetus + + + 189121 + + + + 157770 + + + + 63081 + + + + 200152 + + + + + Hoplias + + + 206989 + + + + 147344 + + + + 206267 + + + + 207317 + + + + + Lebiasina + + + 70715 + + + + + Nannostomus + + + 204292 + + + + + 2 + Oligosarcus + + + 206200 + + + + + Parodon + + + 205671 + + + + + 5A + Phenacogaster + + + 204701 + + + + + Prochilodus + + + 205861 + + + + + Pyrrhulina + + + 207758 + + + + + Schizodon + + + 207939 + + + + + Steindachnerina + + + 203531 + + + + + 4 + Tetragonopterus + + + 207890 + + + + + Xenocharax + + + 48020 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4 + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + ≥2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3A + + + + + + + + + + + + + + + + + + 3B + + + + + + + + + + + + + + + + + + + + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ≤9 + + + + + + + + + + + + + + + ≥10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ≥19 + + + + + + + + + + + + + + + ≤13 + + + + + + + + + + + + + + + + + <38 + + + + + + + + + + + + + + + ≥38 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ≥2 + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + ≤2 + + + + + + + + + + + + + + + 3 + + + + + + + + + + + + + + + + + 2 pairs + + + + + + + + + + + + + + + 1 pair + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + no description in publication, but state 2 present in publication matrix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + for the taxa Crenuchus spilurus, publication matrix identifies state 2 for character 73; however, there is not a state 2 description in the publication character and state list + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file