package org.phenoscape.io; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import java.math.BigDecimal; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.regex.Pattern; import javax.xml.namespace.QName; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.xmlbeans.XmlAnySimpleType; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlOptions; import org.bioontologies.obd.schema.pheno.PhenotypeCharacterDocument.PhenotypeCharacter; import org.bioontologies.obd.schema.pheno.PhenotypeManifestationDocument.PhenotypeManifestation; import org.jdom2.Attribute; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.Namespace; import org.jdom2.input.SAXBuilder; import org.jdom2.output.XMLOutputter; import org.nexml.schema_2009.AbstractBlock; import org.nexml.schema_2009.AbstractChar; import org.nexml.schema_2009.AbstractMapping; import org.nexml.schema_2009.AbstractObs; import org.nexml.schema_2009.AbstractObsMatrix; import org.nexml.schema_2009.AbstractObsRow; import org.nexml.schema_2009.AbstractPolymorphicStateSet; import org.nexml.schema_2009.AbstractState; import org.nexml.schema_2009.AbstractStates; import org.nexml.schema_2009.AbstractUncertainStateSet; import org.nexml.schema_2009.Annotated; import org.nexml.schema_2009.IntTree; import org.nexml.schema_2009.NexmlDocument; import org.nexml.schema_2009.StandardCells; import org.nexml.schema_2009.StandardChar; import org.nexml.schema_2009.StandardMapping; import org.nexml.schema_2009.StandardMatrixObsRow; import org.nexml.schema_2009.StandardObs; import org.nexml.schema_2009.StandardPolymorphicStateSet; import org.nexml.schema_2009.StandardState; import org.nexml.schema_2009.StandardStates; import org.nexml.schema_2009.StandardUncertainStateSet; import org.nexml.schema_2009.Taxa; import org.nexml.schema_2009.TreeIntEdge; import org.nexml.schema_2009.TreeNode; import org.nexml.schema_2009.Trees; import org.obo.datamodel.LinkedObject; import org.obo.datamodel.impl.OBOClassImpl; import org.phenoscape.io.NeXMLUtil.Annotatable; import org.phenoscape.model.Association; import org.phenoscape.model.AssociationSupport; import org.phenoscape.model.Character; import org.phenoscape.model.DataSet; import org.phenoscape.model.MultipleState; import org.phenoscape.model.MultipleState.MODE; import org.phenoscape.model.Phenotype; import org.phenoscape.model.Specimen; import org.phenoscape.model.State; import org.phenoscape.model.Taxon; import org.phenoscape.model.Tree; import org.w3c.dom.Attr; public class NeXMLWriter { private final NexmlDocument xmlDoc; private final String charactersBlockID; private DataSet data; private String generator; private final XmlOptions options = new XmlOptions(); private final Map<Character, AbstractStates> statesBlocksByCharacter = new HashMap<Character, AbstractStates>(); private final Map<Taxon, String> otuIDsByTaxon = new HashMap<Taxon, String>(); private final Set<String> usedUncertainStateSets = new HashSet<String>(); public NeXMLWriter(String charactersBlockID) { this(charactersBlockID, NexmlDocument.Factory.newInstance()); } public NeXMLWriter(String charactersBlockID, NexmlDocument startingDoc) { this.charactersBlockID = charactersBlockID; this.xmlDoc = startingDoc; this.options.setSavePrettyPrint(); final Map<String, String> suggestedPrefixes = new HashMap<String, String>(); suggestedPrefixes.put("http://www.nexml.org/2009", ""); suggestedPrefixes.put(NeXMLUtil.DARWIN_CORE_NAMESPACE, NeXMLUtil.DARWIN_CORE_PREFIX); suggestedPrefixes.put(NeXMLUtil.DUBLIN_CORE_NAMESPACE, NeXMLUtil.DUBLIN_CORE_PREFIX); suggestedPrefixes.put(NeXMLUtil.PHENOSCAPE_NAMESPACE, NeXMLUtil.PHENOSCAPE_PREFIX); suggestedPrefixes.put(NeXMLUtil.RDFS_NAMESPACE, NeXMLUtil.RDFS_PREFIX); suggestedPrefixes.put(NeXMLUtil.OBO_NAMESPACE, NeXMLUtil.OBO_PREFIX); this.options.setSaveAggressiveNamespaces(); this.options.setSaveSuggestedPrefixes(suggestedPrefixes); this.options.setSaveNamespacesFirst(); this.options.setUseDefaultNamespace(); final Map<String, String> implicitNamespaces = new HashMap<String, String>(); implicitNamespaces.put("http://www.nexml.org/2009", "nex"); } public void setDataSet(DataSet data) { this.data = data; this.statesBlocksByCharacter.clear(); this.otuIDsByTaxon.clear(); this.usedUncertainStateSets.clear(); } public void setGenerator(String appName) { this.generator = appName; } public void write(File aFile) throws IOException { final OutputStream stream = new FileOutputStream(aFile); this.write(stream); stream.close(); } public void write(OutputStream aStream) throws IOException { final Document document = this.saveToJDOMDoc(); new XMLOutputter().output(document, aStream); } public void write(Writer aWriter) throws IOException { final Document document = this.saveToJDOMDoc(); new XMLOutputter().output(document, aWriter); } private Document saveToJDOMDoc() throws IOException { try { final StringWriter writer = new StringWriter(); this.constructXMLDoc().save(writer, this.options); writer.close(); final String doc = writer.toString(); final SAXBuilder jdomBuilder = new SAXBuilder(); final Document document = jdomBuilder.build(new StringReader(doc)); final Element nexmlRoot = document.getRootElement(); nexmlRoot.addNamespaceDeclaration(Namespace.getNamespace(NeXMLUtil.DARWIN_CORE_PREFIX, NeXMLUtil.DARWIN_CORE_NAMESPACE)); nexmlRoot.addNamespaceDeclaration(Namespace.getNamespace(NeXMLUtil.DUBLIN_CORE_PREFIX, NeXMLUtil.DUBLIN_CORE_NAMESPACE)); nexmlRoot.addNamespaceDeclaration(Namespace.getNamespace(NeXMLUtil.PHENOSCAPE_PREFIX, NeXMLUtil.PHENOSCAPE_NAMESPACE)); nexmlRoot.addNamespaceDeclaration(Namespace.getNamespace(NeXMLUtil.RDFS_PREFIX, NeXMLUtil.RDFS_NAMESPACE)); nexmlRoot.addNamespaceDeclaration(Namespace.getNamespace(NeXMLUtil.OBO_PREFIX, NeXMLUtil.OBO_NAMESPACE)); final Map<String, String> declaredNamespaceURIs = new HashMap<String, String>(); for (Namespace namespace : nexmlRoot.getNamespacesIntroduced()) { declaredNamespaceURIs.put(namespace.getURI(), namespace.getPrefix()); } for (Element child : nexmlRoot.getChildren()) { recursivelyRemoveRedundantNamespaces(child, declaredNamespaceURIs); } return document; } catch (JDOMException e) { throw new IOException(e); } } private void recursivelyRemoveRedundantNamespaces(Element element, Map<String, String> declaredNamespaces) { for (Element child : element.getChildren()) { recursivelyRemoveRedundantNamespaces(child, declaredNamespaces); } for (Attribute attribute : element.getAttributes()) { final String value = attribute.getValue(); for (Namespace namespace : element.getNamespacesInScope()) { final String possiblePrefix = namespace.getPrefix() + ":"; if (value.startsWith(possiblePrefix)) { final Namespace valueNamespace = namespace; if (declaredNamespaces.keySet().contains(valueNamespace.getURI())) { final String newPrefix = declaredNamespaces.get(valueNamespace.getURI()) == "" ? "" : declaredNamespaces.get(valueNamespace.getURI()) + ":"; final String newValue = value.replaceFirst(Pattern.quote(possiblePrefix), newPrefix); attribute.setValue(newValue); break; } } } } final Set<Namespace> namespacesToRemove = new HashSet<Namespace>(); for (Namespace namespace : element.getNamespacesIntroduced()) { if (declaredNamespaces.keySet().contains(namespace.getURI())) { namespacesToRemove.add(namespace); } } for (Namespace namespace : namespacesToRemove) { element.removeNamespaceDeclaration(namespace); } } private NexmlDocument constructXMLDoc() { final NexmlDocument newDoc = (NexmlDocument) (xmlDoc.copy()); if (newDoc.getNexml() == null) { newDoc.addNewNexml(); } if (this.generator != null) { newDoc.getNexml().setGenerator(this.generator); } newDoc.getNexml().setVersion(BigDecimal.valueOf(0.9)); XmlCursor cursor = newDoc.newCursor(); if (cursor.toFirstChild()) { cursor.setAttributeText(new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation"), "http://www.nexml.org/2009 http://www.nexml.org/2009/nexml.xsd http://www.bioontologies.org/obd/schema/pheno http://purl.org/phenoscape/phenoxml.xsd"); } final Annotatable annotatableNexml = new Annotatable(newDoc.getNexml()); NeXMLUtil.setMetadata(annotatableNexml, NeXMLUtil.CURATORS_PREDICATE, this.data.getCurators()); NeXMLUtil.unsetMetadata(annotatableNexml, NeXMLUtil.PUBLICATION_SOURCE_PREDICATE); final Map<QName, Object> publicationData = new HashMap<QName, Object>(); if (this.data.getPublicationLabel() != null) { publicationData.put(NeXMLUtil.PUBLICATION_LABEL_PREDICATE, this.data.getPublicationLabel()); } if (this.data.getPublicationCitation() != null) { publicationData.put(NeXMLUtil.PUBLICATION_CITATION_PREDICATE, this.data.getPublicationCitation()); } if (this.data.getPublicationURI() != null) { publicationData.put(NeXMLUtil.PUBLICATION_URI_PREDICATE, this.data.getPublicationURI()); } if (!publicationData.isEmpty()) { NeXMLUtil.addMetadata(annotatableNexml, NeXMLUtil.PUBLICATION_SOURCE_PREDICATE, publicationData); } NeXMLUtil.setMetadata(annotatableNexml, NeXMLUtil.DC_DESCRIPTION_PREDICATE, this.data.getPublicationNotes()); final AbstractBlock charBlock = NeXMLUtil.findOrCreateCharactersBlock( newDoc, this.charactersBlockID); this.writeCharacters(charBlock); final String taxaID; if ((charBlock.getOtus() == null) || (charBlock.getOtus().equals(""))) { taxaID = "t" + UUID.randomUUID().toString(); charBlock.setOtus(taxaID); } else { taxaID = charBlock.getOtus(); } final Taxa taxaBlock = NeXMLUtil.findOrCreateTaxa(newDoc, taxaID); this.writeTaxa(taxaBlock); // move taxa ahead of characters final XmlCursor firstCharCursor = newDoc.getNexml() .getCharactersArray()[0].newCursor(); final XmlCursor taxaCursor = taxaBlock.newCursor(); taxaCursor.moveXml(firstCharCursor); for (AbstractStates statesBlock : charBlock.getFormat() .getStatesArray()) { final AbstractPolymorphicStateSet[] existingSets = statesBlock .getPolymorphicStateSetArray(); final List<AbstractPolymorphicStateSet> usedSets = new ArrayList<AbstractPolymorphicStateSet>(); for (AbstractPolymorphicStateSet stateSet : existingSets) { if (this.usedUncertainStateSets.contains(stateSet.getId())) { usedSets.add(stateSet); } } statesBlock.setPolymorphicStateSetArray(usedSets .toArray(new AbstractPolymorphicStateSet[] {})); } for (AbstractStates statesBlock : charBlock.getFormat() .getStatesArray()) { final AbstractUncertainStateSet[] existingSets = statesBlock .getUncertainStateSetArray(); final List<AbstractUncertainStateSet> usedSets = new ArrayList<AbstractUncertainStateSet>(); for (AbstractUncertainStateSet stateSet : existingSets) { if (this.usedUncertainStateSets.contains(stateSet.getId())) { usedSets.add(stateSet); } } statesBlock.setUncertainStateSetArray(usedSets .toArray(new AbstractUncertainStateSet[] {})); } final Trees treesBlock = NeXMLUtil.findOrCreateTreesBlock(newDoc, "t" + UUID.randomUUID().toString()); treesBlock.setOtus(taxaID); this.writeTrees(treesBlock); return newDoc; } private void writeCharacters(AbstractBlock charBlock) { final List<AbstractChar> existingChars = Arrays.asList(charBlock .getFormat().getCharArray()); final List<AbstractStates> existingStatesList = Arrays.asList(charBlock .getFormat().getStatesArray()); final List<AbstractChar> newCharacters = new ArrayList<AbstractChar>(); final List<AbstractStates> newStatesBlocks = new ArrayList<AbstractStates>(); final Set<String> usedStatesIDs = new HashSet<String>(); for (Character character : this.data.getCharacters()) { final AbstractChar xmlChar = this.findOrCreateCharWithID( existingChars, character.getNexmlID()); newCharacters.add(xmlChar); xmlChar.setLabel(character.getLabel()); this.writeDenotes(xmlChar, character.getDenotes()); this.writeComment(xmlChar, character.getComment()); this.writeFigure(xmlChar, character.getFigure()); this.writeDiscussion(xmlChar, character.getDiscussion()); final AbstractStates statesBlock = this .findOrCreateStatesBlockWithID(existingStatesList, character.getStatesNexmlID()); final AbstractStates usableStatesBlock; if (usedStatesIDs.contains(statesBlock.getId())) { usableStatesBlock = (AbstractStates) (statesBlock.copy()); usableStatesBlock.setId("sb" + UUID.randomUUID().toString()); } else { usableStatesBlock = statesBlock; } this.statesBlocksByCharacter.put(character, usableStatesBlock); newStatesBlocks.add(usableStatesBlock); usedStatesIDs.add(usableStatesBlock.getId()); xmlChar.setStates(usableStatesBlock.getId()); final List<AbstractState> existingStates = Arrays .asList(usableStatesBlock.getStateArray()); final List<AbstractState> newStates = new ArrayList<AbstractState>(); for (State state : character.getStates()) { final AbstractState xmlState = this.findOrCreateStateWithID( existingStates, state.getNexmlID()); newStates.add(xmlState); xmlState.setLabel(state.getLabel()); this.writeComment(xmlState, state.getComment()); this.writeFigure(xmlState, state.getFigure()); this.writeSymbol(xmlState, state.getSymbol() != null ? state.getSymbol() : "0"); this.writePhenotypes(xmlState, state); } usableStatesBlock.setStateArray(newStates .toArray(new AbstractState[] {})); } charBlock.getFormat().setCharArray( newCharacters.toArray(new AbstractChar[] {})); if (charBlock instanceof StandardCells) { final StandardCells cells = (StandardCells) charBlock; final AbstractObsMatrix matrix = cells.getMatrix() != null ? cells .getMatrix() : cells.addNewMatrix(); this.writeMatrix(matrix); } charBlock.getFormat().setStatesArray( newStatesBlocks.toArray(new AbstractStates[] {})); } private void writeMatrix(AbstractObsMatrix matrix) { final List<AbstractObsRow> existingRows = Arrays.asList(matrix.getRowArray()); final List<AbstractObsRow> newRows = new ArrayList<AbstractObsRow>(); for (Taxon taxon : this.data.getTaxa()) { final AbstractObsRow xmlRow = this.findOrCreateRowForTaxon(existingRows, taxon.getNexmlID()); newRows.add(xmlRow); final List<AbstractObs> existingCells = Arrays.asList(xmlRow.getCellArray()); final List<AbstractObs> newCells = new ArrayList<AbstractObs>(); for (Character character : this.data.getCharacters()) { final State state = this.data.getStateForTaxon(taxon, character); if (state != null) { final AbstractObs xmlCell = this.findOrCreateCellForCharacter(existingCells, character.getNexmlID()); final XmlAnySimpleType xmlState = XmlAnySimpleType.Factory.newInstance(); final Set<State> statesForAssociations = new HashSet<State>(); if (state instanceof MultipleState) { xmlState.setStringValue(this.findOrCreateMultiValueState(character, (MultipleState)state).getId()); for (State subState : ((MultipleState) state).getStates()) { statesForAssociations.add(subState); } } else { xmlState.setStringValue(state.getNexmlID()); statesForAssociations.add(state); } xmlCell.setState(xmlState); final Annotatable annotatableCell = new Annotatable(xmlCell); NeXMLUtil.unsetMetadata(annotatableCell, NeXMLUtil.ENTAILED_BY_PREDICATE); for (State stateForAssociations : statesForAssociations) { final Association association = new Association(taxon.getNexmlID(), character.getNexmlID(), stateForAssociations.getNexmlID()); final Set<AssociationSupport> supports = this.data.getAssociationSupport().get(association); if (supports != null) { for (AssociationSupport support : this.data.getAssociationSupport().get(association)) { final Map<QName, Object> supportMeta = new HashMap<QName, Object>(); supportMeta.put(NeXMLUtil.DC_IDENTIFIER, stateForAssociations.getNexmlID()); supportMeta.put(NeXMLUtil.DC_DESCRIPTION_PREDICATE, support.getDescriptionText()); supportMeta.put(NeXMLUtil.DC_SOURCE_PREDICATE, support.getDescriptionSource()); supportMeta.put(NeXMLUtil.IS_DIRECT_PREDICATE, support.isDirect()); NeXMLUtil.addMetadata(annotatableCell, NeXMLUtil.ENTAILED_BY_PREDICATE, supportMeta); } } } newCells.add(xmlCell); } } xmlRow.setCellArray(newCells.toArray(new AbstractObs[] {})); } matrix.setRowArray(newRows.toArray(new AbstractObsRow[] {})); } private void writeTaxa(Taxa taxaBlock) { final List<org.nexml.schema_2009.Taxon> existingOTUs = Arrays .asList(taxaBlock.getOtuArray()); final List<org.nexml.schema_2009.Taxon> newOTUs = new ArrayList<org.nexml.schema_2009.Taxon>(); for (Taxon taxon : this.data.getTaxa()) { final org.nexml.schema_2009.Taxon otu = this.findOrCreateOTUWithID( existingOTUs, taxon.getNexmlID()); newOTUs.add(otu); otu.setLabel(taxon.getPublicationName()); this.writeOBOID(otu, taxon); this.writeSpecimens(otu, taxon); this.writeComment(otu, taxon.getComment()); this.writeFigure(otu, taxon.getFigure()); this.writeMatrixTaxon(otu, taxon.getMatrixTaxonName()); this.otuIDsByTaxon.put(taxon, taxon.getNexmlID()); } taxaBlock.setOtuArray(newOTUs .toArray(new org.nexml.schema_2009.Taxon[] {})); } private void writeTrees(Trees treesBlock) { final List<IntTree> treeList = new ArrayList<IntTree>(); for (Tree tree : this.data.getTrees()) { final IntTree treeXML = IntTree.Factory.newInstance(); treeList.add(treeXML); treeXML.setLabel(StringUtils.stripToNull(tree.getLabel())); treeXML.setId(tree.getNexmlID()); final Map<LinkedObject, TreeNode> treeNodes = new HashMap<LinkedObject, TreeNode>(); final Map<LinkedObject, LinkedObject> topology = tree.getTopology(); final List<TreeIntEdge> edges = new ArrayList<TreeIntEdge>(); for (Entry<LinkedObject, LinkedObject> entry : topology.entrySet()) { final LinkedObject childNode = entry.getKey(); final LinkedObject parentNode = entry.getValue(); final TreeNode sourceNode; if (treeNodes.containsKey(childNode)) { sourceNode = treeNodes.get(childNode); } else { sourceNode = TreeNode.Factory.newInstance(); sourceNode.setLabel(childNode.getName()); sourceNode.setId(childNode.getID() + "#" + UUID.randomUUID().toString()); treeNodes.put(childNode, sourceNode); } final TreeNode targetNode; if (treeNodes.containsKey(parentNode)) { targetNode = treeNodes.get(parentNode); } else { targetNode = TreeNode.Factory.newInstance(); targetNode.setLabel(parentNode.getName()); targetNode.setId(parentNode.getID() + "#" + UUID.randomUUID().toString()); treeNodes.put(parentNode, targetNode); } final TreeIntEdge newEdge = TreeIntEdge.Factory.newInstance(); newEdge.setTarget(sourceNode.getId()); newEdge.setSource(targetNode.getId()); final XmlAnySimpleType oneXML = XmlAnySimpleType.Factory .newInstance(); oneXML.setStringValue("1"); newEdge.setLength(oneXML); edges.add(newEdge); } for (Taxon taxon : this.data.getTaxa()) { final String otu = this.otuIDsByTaxon.get(taxon); if (otu == null) { log().error("No otu for taxon: " + taxon); } if (taxon.getValidName() != null) { final TreeNode treeNode = treeNodes.get(taxon .getValidName()); if (treeNode == null) { log().error("No tree node for taxon: " + taxon); } if (topology.containsValue(taxon.getValidName())) { // this taxon is an internal node final TreeNode extraNode = TreeNode.Factory .newInstance(); extraNode.setLabel(taxon.getValidName().getName() + " species"); final String uuid = UUID.randomUUID().toString(); extraNode.setId(taxon.getValidName().getID() + "#" + uuid); treeNodes.put(new OBOClassImpl(uuid), extraNode); extraNode.setOtu(otu); final TreeIntEdge newEdge = TreeIntEdge.Factory .newInstance(); newEdge.setTarget(extraNode.getId()); newEdge.setSource(treeNode.getId()); final XmlAnySimpleType oneXML = XmlAnySimpleType.Factory .newInstance(); oneXML.setStringValue("1"); newEdge.setLength(oneXML); edges.add(newEdge); } else { treeNode.setOtu(otu); } } } treeXML.setNodeArray(treeNodes.values().toArray(new TreeNode[] {})); treeXML.setEdgeArray(edges.toArray(new TreeIntEdge[] {})); } treesBlock.setTreeArray(treeList.toArray(new IntTree[] {})); } private AbstractObsRow findOrCreateRowForTaxon(List<AbstractObsRow> list, String id) { for (AbstractObsRow row : list) { if (id.equals(row.getOtu())) { return row; } } final AbstractObsRow newRow = StandardMatrixObsRow.Factory .newInstance(); newRow.setId("r" + UUID.randomUUID().toString()); newRow.setOtu(id); return newRow; } private AbstractObs findOrCreateCellForCharacter(List<AbstractObs> list, String id) { for (AbstractObs cell : list) { if (id.equals(cell.getChar().getStringValue())) { return cell; } } final AbstractObs newCell = StandardObs.Factory.newInstance(); final XmlAnySimpleType charXML = XmlAnySimpleType.Factory.newInstance(); charXML.setStringValue(id); newCell.setChar(charXML); return newCell; } private org.nexml.schema_2009.Taxon findOrCreateOTUWithID( List<org.nexml.schema_2009.Taxon> list, String id) { for (org.nexml.schema_2009.Taxon otu : list) { if (otu.getId().equals(id)) { return otu; } } final org.nexml.schema_2009.Taxon newOTU = org.nexml.schema_2009.Taxon.Factory .newInstance(); newOTU.setId(id); return newOTU; } private AbstractChar findOrCreateCharWithID(List<AbstractChar> list, String id) { for (AbstractChar character : list) { if (character.getId().equals(id)) { return character; } } final AbstractChar newCharacter = StandardChar.Factory.newInstance(); newCharacter.setId(id); return newCharacter; } private AbstractStates findOrCreateStatesBlockWithID( List<AbstractStates> list, String id) { for (AbstractStates statesBlock : list) { if (statesBlock.getId().equals(id)) { return statesBlock; } } final AbstractStates newStatesBlock = StandardStates.Factory .newInstance(); newStatesBlock.setId(id); return newStatesBlock; } private AbstractState findOrCreateStateWithID(List<AbstractState> list, String id) { for (AbstractState state : list) { if (state.getId().equals(id)) { return state; } } final AbstractState newState = StandardState.Factory.newInstance(); newState.setId(id); return newState; } private AbstractState findOrCreateMultiValueState(Character character, MultipleState state) { final AbstractStates block = this.statesBlocksByCharacter.get(character); final AbstractUncertainStateSet[] sets = (state.getMode() == MODE.POLYMORPHIC) ? block.getPolymorphicStateSetArray() : block.getUncertainStateSetArray(); for (AbstractUncertainStateSet set : sets) { if (this.multipleStateSetMatches(state, set)) { this.usedUncertainStateSets.add(set.getId()); return set; } } final AbstractUncertainStateSet set; if (state.getMode() == MODE.POLYMORPHIC) { final StandardPolymorphicStateSet polymorphicSet = StandardPolymorphicStateSet.Factory.newInstance(); set = polymorphicSet; polymorphicSet.setId("s" + UUID.randomUUID().toString()); writeSymbol(polymorphicSet, state.getSymbol()); final AbstractPolymorphicStateSet[] oldStates = block.getPolymorphicStateSetArray(); final List<AbstractPolymorphicStateSet> newStates = new ArrayList<AbstractPolymorphicStateSet>(); newStates.addAll(Arrays.asList(oldStates)); final List<AbstractMapping> mappings = new ArrayList<AbstractMapping>(); for (State substate : state.getStates()) { final StandardMapping mapping = StandardMapping.Factory .newInstance(); mapping.setState(substate.getNexmlID()); mappings.add(mapping); } set.setMemberArray(mappings.toArray(new AbstractMapping[] {})); newStates.add(polymorphicSet); block.setPolymorphicStateSetArray(newStates.toArray(new AbstractPolymorphicStateSet[] {})); } else { final StandardUncertainStateSet uncertainSet = StandardUncertainStateSet.Factory.newInstance(); set = uncertainSet; uncertainSet.setId("s" + UUID.randomUUID().toString()); writeSymbol(uncertainSet, state.getSymbol()); final AbstractUncertainStateSet[] oldStates = block.getUncertainStateSetArray(); final List<AbstractUncertainStateSet> newStates = new ArrayList<AbstractUncertainStateSet>(); newStates.addAll(Arrays.asList(oldStates)); final List<AbstractMapping> mappings = new ArrayList<AbstractMapping>(); for (State substate : state.getStates()) { final StandardMapping mapping = StandardMapping.Factory.newInstance(); mapping.setState(substate.getNexmlID()); mappings.add(mapping); } set.setMemberArray(mappings.toArray(new AbstractMapping[] {})); newStates.add(uncertainSet); block.setUncertainStateSetArray(newStates.toArray(new AbstractUncertainStateSet[] {})); } this.usedUncertainStateSets.add(set.getId()); return set; } private boolean multipleStateSetMatches(MultipleState state, AbstractUncertainStateSet set) { final Set<String> stateIDs = new HashSet<String>(); for (State substate : state.getStates()) { stateIDs.add(substate.getNexmlID()); } final Set<String> setStateIDs = new HashSet<String>(); for (AbstractMapping mapping : set.getMemberArray()) { setStateIDs.add(mapping.getState()); } return stateIDs.equals(setStateIDs); } private void writeOBOID(org.nexml.schema_2009.Taxon otu, Taxon taxon) { final Annotatable annotatableOTU = new Annotatable(otu); if (taxon.getValidName() == null) { NeXMLUtil.unsetMetadata(annotatableOTU, NeXMLUtil.VALID_NAME_PREDICATE); } else { NeXMLUtil.setMetadata(annotatableOTU, NeXMLUtil.VALID_NAME_PREDICATE, NeXMLUtil.oboURI(taxon.getValidName())); } } private void writeSpecimens(org.nexml.schema_2009.Taxon otu, Taxon taxon) { final Annotatable annotatableOTU = new Annotatable(otu); NeXMLUtil.unsetMetadata(annotatableOTU, NeXMLUtil.SPECIMEN_PREDICATE); for (Specimen specimen : taxon.getSpecimens()) { final Map<QName, Object> specimenData = new HashMap<QName, Object>(); if (specimen.getCollectionCode() != null) { specimenData.put(NeXMLUtil.COLLECTION_PREDICATE, NeXMLUtil.oboURI(specimen.getCollectionCode())); } if (specimen.getCatalogID() != null) { specimenData.put(NeXMLUtil.ACCESSION_PREDICATE, specimen.getCatalogID()); } if (specimen.getComment() != null) { specimenData.put(NeXMLUtil.COMMENT_PREDICATE, specimen.getComment()); } NeXMLUtil.addMetadata(annotatableOTU, NeXMLUtil.SPECIMEN_PREDICATE, specimenData); } } private void writeDenotes(Annotated node, URI term) { final Annotatable annotatableNode = new Annotatable(node); if (term == null) { NeXMLUtil.unsetMetadata(annotatableNode, NeXMLUtil.DENOTES_PREDICATE); } else { NeXMLUtil.setMetadata(annotatableNode, NeXMLUtil.DENOTES_PREDICATE, term); } } private void writeComment(Annotated node, String comment) { final Annotatable annotatableNode = new Annotatable(node); if ((comment == null) || (comment.equals(""))) { NeXMLUtil.unsetMetadata(annotatableNode, NeXMLUtil.COMMENT_PREDICATE); } else { NeXMLUtil.setMetadata(annotatableNode, NeXMLUtil.COMMENT_PREDICATE, comment); } } private void writeFigure(Annotated node, String figure) { final Annotatable annotatableNode = new Annotatable(node); if ((figure == null) || (figure.equals(""))) { NeXMLUtil .unsetMetadata(annotatableNode, NeXMLUtil.FIGURE_PREDICATE); } else { NeXMLUtil.setMetadata(annotatableNode, NeXMLUtil.FIGURE_PREDICATE, figure); } } private void writeDiscussion(Annotated node, String discussion) { final Annotatable annotatableNode = new Annotatable(node); if ((discussion == null) || (discussion.equals(""))) { NeXMLUtil.unsetMetadata(annotatableNode, NeXMLUtil.DISCUSSION_PREDICATE); } else { NeXMLUtil.setMetadata(annotatableNode, NeXMLUtil.DISCUSSION_PREDICATE, discussion); } } private void writeMatrixTaxon(org.nexml.schema_2009.Taxon otu, String matrixTaxon) { final Annotatable annotatableOTU = new Annotatable(otu); if ((matrixTaxon == null) || (matrixTaxon.equals(""))) { NeXMLUtil.unsetMetadata(annotatableOTU, NeXMLUtil.MATRIX_NAME_PREDICATE); } else { NeXMLUtil.setMetadata(annotatableOTU, NeXMLUtil.MATRIX_NAME_PREDICATE, matrixTaxon); } } private void writePhenotypes(AbstractState xmlState, State state) { final Annotatable annotatableState = new Annotatable(xmlState); if (state.getPhenotypes().isEmpty()) { NeXMLUtil.unsetMetadata(annotatableState, NeXMLUtil.PHENOTYPE_PREDICATE); return; } else { final List<PhenotypeCharacter> pcs = new ArrayList<PhenotypeCharacter>(); for (Phenotype phenotype : state.getPhenotypes()) { final PhenotypeCharacter pc = PhenoXMLAdapter .createPhenotypeCharacter(phenotype); if (pc != null) { pcs.add(pc); } } final org.bioontologies.obd.schema.pheno.PhenotypeDocument.Phenotype phenoXML = PhenoXMLAdapter .createPhenotype(pcs); // for some reason the PhenoXML Phenotype appears only as a // DocumentFragment instead of Element until it's stuck into a // PhenotypeManifestation final PhenotypeManifestation scratchPM = PhenotypeManifestation.Factory .newInstance(); scratchPM.setPhenotype(phenoXML); NeXMLUtil.setMetadata(annotatableState, NeXMLUtil.PHENOTYPE_PREDICATE, scratchPM.getPhenotype() .getDomNode()); } } /** * The NeXML schema forces symbols to be an integer, but I believe this is * incorrect. This method should allow us to write any kind of symbol. */ private void writeSymbol(AbstractState state, String symbolValue) { final XmlAnySimpleType symbol = state.getSymbol() != null ? state.getSymbol() : state.addNewSymbol(); final Attr attribute = (Attr) (symbol.getDomNode()); attribute.setValue(symbolValue); } private Logger log() { return Logger.getLogger(this.getClass()); } }