package textbender.d.transfer; // Copyright 2007, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import java.io.*; import java.util.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.stream.*; import org.w3c.dom.*; import org.w3c.dom.ls.*; import textbender.d.gene.*; import textbender.d.revision.*; import textbender.g.lang.*; import textbender.g.util.logging.*; import textbender.g.xml.dom.*; import static textbender._.Textbender.TEXTBENDER_NAMESPACE; /** Utilities for conducting recombinant transfers. */ public @ThreadSafe class Transfer { private Transfer() {} /** Returns true if the gene's sequence has changed enough * that the mutation should be recorded in transfer ancestry. *

* Errors/doubts will fall on the side of mutation. * Worst outcome is, caller will add redundant info to ancestry, * which later tracers can prune away. *

* * @param gene whose sequence to inspect for mutation * @param g gene meta-data ('g') element, of gene * @param gList list of all gene meta-data ('g') elements in the document * @param mutantAbstractor to use * @param domLS DOM implementation to use * @param parser parser to use * @param b string builder to use, overwriting its existing content * * @return true if abstract form of sequence differs from that * of all immediate transfer ancestors, * or if there are no such ancestors * * @see d/transfer/note.xht#Maintenance-of-Ancestry-Records */ public static boolean isTransferMutant( final Element gene, final Element g, final List gList, final MutantAbstractor mutantAbstractor, final DOMImplementationLS domLS, final LSParser parser, final StringBuilder b ) { final Object tSSAbstract; { final String tSSEscaped = g.getAttributeNS( null, "tSS" ); if( tSSEscaped.length() == 0 ) return true; // original sequence, therefore mutant b.delete( 0, Integer.MAX_VALUE ); b.append( tSSEscaped ); Gene.unescape_embeddable( b ); final LSInput lsInput = domLS.createLSInput(); lsInput.setStringData( b.toString() ); try { Document tSSEmbeddableDocument = parser.parse( lsInput ); tSSAbstract = mutantAbstractor.abstract_transferMutant ( tSSEmbeddableDocument.getDocumentElement(), b ); } catch( LSException x ) { final String gString = gene.getAttributeNS( TEXTBENDER_NAMESPACE, "g" ); LoggerX.i(Transfer.class).warning ( "Bad tSS for gene g=" + gString + ":" + "\n " + tSSEscaped + "\n" + "Exception from parser: " + x ); return true; // assume a mutant, per API doc } } final Object geneAbstract; { Element e = (Element)gene.cloneNode( /*deeply*/true ); Gene.abstract_embeddable( e, gList ); geneAbstract = mutantAbstractor.abstract_transferMutant( e, b ); } return !geneAbstract.equals( tSSAbstract ); } /** Records a gene as a transfer source. * This entails 1) overwriting the target gene's transfer source sequence * 'tSS' attribute; and 2) recording the source sequence * as the immediate ancestor of the target, with all prior ancestry * contained within it. * * @param sourceGene to record as the transfer source sequence * @param sourceGG the 'g' list ('gg') element of the source document * @param sourceGList list of all gene meta-data ('g') elements * in source document * @param targetG gene meta-data ('g') element, of targt gene * @param nodeTransformSource to use, via its setNode() * @param eSS identity transformer to use, as a string serializer * for creating embedded sequence copy * @param eSSWriter eSS string writer * @param eSSResult result, pre-set to eSSWriter * @param b string builder to use, overwriting its existing content */ public static void recordAsTransferSource( final Element sourceGene, final Element sourceGG, final List sourceGList, final Element targetG, final DOMSource nodeTransformSource, final Transformer eSS, final StringWriter eSSWriter, final StreamResult eSSResult, final StringBuilder b ) throws TransformerException // from transform { // record source as transfer source sequence // ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` Element sourceGeneCopy = (Element)sourceGene.cloneNode( /*deeply*/true ); Gene.abstract_embeddable( sourceGeneCopy, sourceGList ); nodeTransformSource.setNode( sourceGeneCopy ); eSSWriter.flush(); eSSWriter.getBuffer().delete( 0, Integer.MAX_VALUE ); eSS.transform( nodeTransformSource, eSSResult ); b.delete( 0, Integer.MAX_VALUE ); eSSWriter.flush(); b.append( eSSWriter.toString() ); Gene.escape_embeddable( b ); targetG.setAttributeNS( null, "tSS", b.toString() ); // record source as immediate ancestor // ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` final Document targetDocument = targetG.getOwnerDocument(); Element immediateAncestor = targetDocument.createElementNS ( TEXTBENDER_NAMESPACE, DOM.buildElementPrefix ( targetG, TEXTBENDER_NAMESPACE, b ).append( "a" ).toString() ); immediateAncestor.setAttributeNS ( null, "g", sourceGene.getAttributeNS( TEXTBENDER_NAMESPACE, "g" )); ElementX.duplicateAttributeNS( sourceGG, null, "gR", immediateAncestor, b ); final Element sourceRevisionLine = Revision.findRevisionLine ( /*sourceMetaData*/(Element)sourceGG.getParentNode() ); if( sourceRevisionLine != null ) // i.e. source doc not malformed { String id = sourceRevisionLine.getAttributeNS( null, "id" ); if( id.length() > 0 ) // i.e. not malformed { immediateAncestor.setAttributeNS( null, "rL", id ); } } // contain prior ancestry within it // ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` NodeX.moveElementsNS ( /*from*/targetG, TEXTBENDER_NAMESPACE, "a", /*to*/immediateAncestor ); // ` ` ` targetG.appendChild( immediateAncestor ); } }