package textbender.a.u.transfer.clipboard; // 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._.*; import textbender.a.u.encoding.Encoder; import textbender.d.gene.*; import textbender.d.transfer.*; import textbender.g.util.*; import textbender.g.util.logging.*; import static textbender._.Textbender.TEXTBENDER_NAMESPACE; /** A clip-gene replacer. */ public final class Replacer { /** Constructs a Replacer for a document. * * @param gg the 'g' list ('gg') element of the document * @param gList live list of gene meta-data ('g') elements in 'gg' * (e.g. backed by a NodeList) * @param domLS DOM implementation to use * @param mutantAbstractor to use * @param parser to use * @param b string builder to use, overwriting its existing content * @param transformerFactory to use * @param uuidStringifier UUID stringifier to use */ public Replacer( Element gg, List gList, StringBuilder b, DOMImplementationLS domLS, MutantAbstractor mutantAbstractor, LSParser parser, TransformerFactory transformerFactory, UUIDStringifier uuidStringifier ) throws IOException, TransformerConfigurationException // IOException IndexBlockMap, TransformerConfigurationException newTransformer { this.gg = gg; this.gList = gList; this.b = b; this.domLS = domLS; this.mutantAbstractor = mutantAbstractor; this.parser = parser; this.uuidStringifier = uuidStringifier; document = gg.getOwnerDocument(); eSS = transformerFactory.newTransformer(); // identity eSS.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" ); eSSWriter = new StringWriter(); eSSResult = new StreamResult( eSSWriter ); } // ------------------------------------------------------------------------------------ /** Transforms a node into a proper gene, if it is a clip gene; * and likewise transforms all of its descendants. */ public void replaceRecursively( final Node node ) throws TransformerException // TransformerException recordAsTransferSourceSequence { // Descendants. // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = for( Node child = node.getFirstChild(); child != null; child = child.getNextSibling() ) { replaceRecursively( child ); } // This Node. // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = if( !( node instanceof Element )) return; final Element newGene = (Element)node; final Attr c = newGene.getAttributeNodeNS( TEXTBENDER_NAMESPACE, "c" ); if( c == null ) return; // Remove clip-index 'c' attribute. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - final String cString = c.getValue(); final long clipIndex = clipIndexEncoder.clipIndexOf( cString ); newGene.removeAttributeNode( c ); // Transform to gene. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - final IndexBlock block = blockMap.getIndexBlock( clipIndex ); final IndexBlock.SourceParse source; final int sourceGIndex; final Element sourceG; final Element newG; { IndexBlock.SourceParse s = null; // so far if( block == null ) LoggerX.i(getClass()).warning( "no source document for clip gene: " + cString ); else { try { s = block.sourceParse( domLS, parser, b, uuidStringifier ); } catch( RuntimeException xR ) { throw xR; } catch( Exception x ) { LoggerX.i(getClass()).log( LoggerX.WARNING, /*message*/"", x ); } if( s == null ) LoggerX.i(getClass()).warning( "could not parse source document for clip gene: " + cString ); } source = s; if( source == null ) { sourceGIndex = -1; sourceG = null; newG = Gene.createG ( /*locus*/uuidStringifier.toBase64Name(UUID.randomUUID()), gg, b ); } else { sourceGIndex = block.gIndexOf( clipIndex ); sourceG = source.gList.get( sourceGIndex ); newG = (Element)document.importNode( sourceG, /*deeply*/true ); } } Gene.linkG( newG, newGene, gg, /*gIndex*/gList.size(), b ); // Adjust left padding, if gene is marginal. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - final String gString = newGene.getAttributeNS( TEXTBENDER_NAMESPACE, "g" ); final int delta = cString.length() - gString.length(); if( delta != 0 && Encoder.isMarginal(newGene) ) Gene.adjustLeftPadding( newGene, delta, b ); // Update transfer ancestry. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if( source == null ) return; // cannot treat as transfer, so it will appear to be original final Element sourceGene = source.geneList.get( sourceGIndex ); if( Transfer.isTransferMutant ( sourceGene, sourceG, source.gList, mutantAbstractor, domLS, parser, b )) { Transfer.recordAsTransferSource ( sourceGene, source.gg, source.gList, newG, nodeTransformSource, eSS, eSSWriter, eSSResult, b ); } } //// P r i v a t e /////////////////////////////////////////////////////////////////////// /** Common string builder. */ private final StringBuilder b; /** Empties and returns the common string builder. */ private StringBuilder bClear() { b.delete( 0, Integer.MAX_VALUE ); return b; } private final IndexBlockMap blockMap = IndexBlockMap.create(); private final ClipIndexEncoder clipIndexEncoder = new ClipIndexEncoder(); private final Document document; private final DOMImplementationLS domLS; private final Transformer eSS; // identity, string serializer for embedded sequence copies private final StreamResult eSSResult; private final StringWriter eSSWriter; private final Element gg; private final List gList; private final MutantAbstractor mutantAbstractor; private final DOMSource nodeTransformSource = new DOMSource(); // for setting via setNode() only private final LSParser parser; private final UUIDStringifier uuidStringifier; }